TT Lab
Get started
Learn Learning paths Courses

Package Management

A Repository Is, In the End, One Index File

Continue in TT Lab

In one line

The substance of an apt repository is a directory with .deb files in it + an index file called Packages. With just that, even file:/// makes an excellent repository.

Why this exists

Air-gapped networks, air gaps, internal standard images, CI caches — environments that cannot or must not reach the internet are more common than you might think. Here there are two options. Move the needed .deb files one by one by hand, or set up the repository itself on the inside. The first becomes unbearable after about three repetitions, and the second, once built, keeps being used.

How it works

What apt actually reads from a repository is surprisingly simple.

저장소 루트/
  Packages         <- 각 .deb 의 control 필드 + Filename + Size + SHA256
  Packages.gz      <- 위의 압축본 (apt 는 압축본을 선호한다)
  Release          <- 저장소 메타데이터 (Origin, Suite, Components, 체크섬)
  *.deb            <- 실제 패키지 파일

Packages is a text file made by concatenating each package's control fields and adding Filename (a path relative to the repository root), Size, and SHA256. The tools that produce it are dpkg-scanpackages (simple) and apt-ftparchive (large scale).

cd /root/repo
dpkg-scanpackages . /dev/null > Packages
gzip -kf Packages

Then you add one line to sources.list.

deb [trusted=yes] file:/root/repo ./

The ./ at the end means a flat repository (a form with no dists/ hierarchy and Packages at the root). [trusted=yes] is a marker saying to trust a repository that has no GPG signature. It is commonly used in labs and temporary internal repositories, but for a production repository you must sign it. With an unsigned repository, you cannot tell if someone swaps a .deb in the middle.

Here you have to distinguish two layers of verification.

Layer What it guarantees Tool
Checksum (SHA256) There was no corruption in transit sha256sum -c
Signature (GPG) The origin is the one I know gpg --verify, apt-key/signed-by

A checksum stored only on the same medium changes along with it if the whole medium is swapped. So the last bulwark of integrity is always the signature.

What it looks like in the field

"I put a package in the repository but the server can't see it." This is the report that comes in most often in air-gapped operations. Most causes are one of two. Either the index was not rebuilt (you forgot dpkg-scanpackages / createrepo_c --update), or the client did not refresh its cache (it did not run apt-get update). Both create the frustrating situation of "the file is definitely there".

The habit of putting the snapshot date in the repository name. If you write it like name=LabHub local (snapshot 2026-08-15), then six months later the question "as of what point in time was this server's content installed?" can be answered with one apt-cache policy line.

Turning imports into an air-gapped network into a procedure

In the previous module we talked about the USB drive going back and forth several times. To get rid of that, you have to turn the import from an improvised errand into a reproducible procedure. The trick is to compute "what to bring" mechanically on the outside in advance.

Receive the result of solving the dependencies in full. On the outside, in an environment of the same distribution and same version, install the needed packages, and collect every file downloaded at that time. Actually doing the install matters. If you only compute a list, it easily goes off because of the difference between what is already on the inside and what is not. So keeping one container on the outside in the same state as the inside is the core device of this work.

Put a manifest in the bundle. The file list, the checksum of each file, which distribution and which point-in-time repository it was fetched from, and what you were trying to install. Without this document, months later nobody knows what that USB drive is.

Verify and install on the inside. Confirm transfer corruption with the manifest's checksums, and, if possible, confirm the signature too, and then refresh the repository. As we saw earlier, the checksum guarantees only against corruption and the signature guarantees the origin.

Keep a record of the installation result. You need to keep a record of which bundle went into which server and when, so that the next time a problem arises, you can answer "as of which point in time is this server". The habit of putting the snapshot date in the repository name, mentioned earlier, is the cheapest form of this record.

The value of this procedure is not only saved time. The most dangerous thing on an air-gapped network is skipping verification because you are in a hurry, and if the procedure exists as a document, then even in a hurry you know for yourself which steps are the ones to skip.

What you will do in the next lab

You do two labs in a row. First you set up a flat repository with a .deb you built yourself and even install from it, and then you download in full the dependencies of an already installed package, make an import bundle with a manifest and checksums, and reproduce an offline install.