TT Lab
Get started
Learn Learning paths Courses

Air-Gapped GPU Driver Installation

Why Air-Gapped Installation Is Hard

Continue in TT Lab

In one line

The real reason air-gapped installation is hard is that the side that computes the dependencies and the side that installs are separated. The information needed for the computation is split between the two sides.

Why this was needed

The request always comes like this. "Just bring me the one driver file."

When you put it in after downloading, you get an error like this.

The following packages have unmet dependencies:
 nvidia-driver-550 : Depends: nvidia-kernel-dkms-550 but it is not installable

You go back out and download that, and it says something else is missing. After three or four round trips with the USB drive, half a day has gone by, and import review happens only once a day.

How it works

Separating computation from installation

A dependency resolver (depsolver) has two inputs.

  1. The entire repository metadata — what each package provides and what it requires
  2. The current state of the target system — what is already installed

The air-gapped side lacks input 1, and the outside lacks input 2. This separation is the essence of the problem.

There are two ways out.

If you start small you take the first, but most organizations end up at the second. That is because, once imports become repeated, the management cost of the first approach grows sharply.

Things that silently drop out

What is most dangerous in approach 1 is packages that drop out silently, without an error.

First, what is already installed disappears from the result. Dependency computation presupposes "the current state of the system doing the computation". Libraries already installed on the connected machine are treated as "not needed" and dropped from the list, and that fact only comes to light inside the air-gapped network.

Second, the result changes if the version differs. If the connected machine is on 24.04 and the install target is on 22.04, the resolved result can differ.

Third, you forget the architecture. If you omit noarch/all packages from the arch specification, Python modules and configuration packages drop out wholesale.

Fourth, the handling of weak dependencies (Recommends). There are cases where they were excluded at computation time but are included by default at install time and so are demanded. The reverse happens too.

So the standard approach is to compute from an empty root. On the apt side, you first extract the list to download with --print-uris and review it, then download with --download-only and check whether that bundle closes within itself (closure).

Integrity — two different questions

There are two questions to ask about a file that has passed through the media.

Question What answers it Tool
Was it damaged in transit? Checksum sha256sum -c
Did this really come from that side? Signature GPG verification, dpkg-sig, rpm --checksig

A checksum contained only on the same media changes along with it if the whole media is swapped. So a checksum alone cannot guarantee the origin. The real guarantee comes from the signature.

And you must also check for files that are on the media but not in the manifest. A missing file soon shows up as an installation failure, but an added file passes without anyone knowing. From the point of view of import review, this is the bigger problem.

The import record

The bundle must carry not only files but also a record.

Item Example Why
Bundle ID gpu-airgap-2026-08-20 The key linking servers and bundles
Snapshot date 2026-08-20 Which point in time the content is from
Target versions ubuntu 24.04 / driver 550.90.07 The premise of reproduction
Creation command apt-get install --download-only ... The core of reproduction
Media hash SHA-256 of the whole archive Integrity per media
Import date and person in charge — An audit requirement

You must be able to answer the question, six months later, "which point in time's content was this server installed from?"

How to make the list before import

The most common failure in air-gapped imports is not integrity but omission. Once something goes in it is hard to bring it out again, so the way you make the list itself becomes the core of the procedure.

Do not count dependencies by hand. Ask the package manager "what is needed to install this" and put its answer in as it is.

# 데비안 계열: 의존성까지 통째로 내려받는다
apt-get install --download-only --reinstall -o Dir::Cache::archives=./pkgs <패키지>
# RHEL 계열
dnf download --resolve --alldeps --destdir ./pkgs <패키지>
# 파이썬: 해시까지 고정해서
pip download -r requirements.txt -d ./wheels --require-hashes

Put container images in by digest. If you put them in by tag, what you downloaded outside and what you use inside can differ.

skopeo copy --all docker://registry/app@sha256:... dir:./images/app

Without --all, only the architecture of this computer right now is put in. If the import target has a different architecture, you meet "manifest unknown" inside.

For the GPU side, version pairing is especially strict. The combination of driver, container toolkit, CUDA, and framework must fit. If even one is off, it shows up as "cannot find the GPU", and inside an air-gapped network you cannot try downloading a different version. Stand up the same combination once outside and import that list as it is.

Put in the installation order as well. Put in together a script that builds an offline repository, configures the system to point at that repository, and installs in order. Finding the order by hand inside takes time and cannot be reproduced.

Put in a way back too. If you raise the driver and a problem occurs, you have to go back to the previous version, and if the previous version's packages are not inside, you cannot roll back. Import the packages of the currently installed version as well.

What it looks like in the field

The epoch trap. Both Debian and RPM can put an epoch before the version (1:550.90.07-0ubuntu1). The epoch does not appear in the file name. Yet in version comparison the epoch takes the highest priority. If you judge "this is newer" by the file name alone, you can be wrong. That is why the manifest must carry the complete version string.

What to look for in the next check

In the quiz that follows, you first distinguish the two inputs of dependency computation, the roles of checksums and signatures, and the two-way verification of the manifest. After checking those criteria of judgment, from the next module on you actually produce the dependency graph and installation order of a package bundle.