Vivian Voss

How We Learned to Install Software

package management freebsd linux sysadmin

Unix Universe

On 17 July 1993, Slackware 1.00 went out on twenty-four floppy disks. It is still maintained, which makes it the oldest surviving Linux distribution, and it still ships a package system that will not resolve a dependency for you. installpkg unpacks an archive, writes down what it put where, and stops. If the program wants a library you do not have, you find that out when you run it.

Three decades of packaging progress have gone past this arrangement without changing its mind. That is worth a moment of respect before we climb, because it is the first honest answer to the question everything else in this piece is arguing about: what should happen when software needs other software?

The Floor: Bookkeeping Without Automation

The refusal is deliberate, and the reasoning given for it has always been that automatic resolution buys convenience with a worse class of failure, the circular requirement and the version conflict that cannot be satisfied at all. Anyone who has watched a package manager propose removing half the desktop in order to install a text editor will grant the point some sympathy.

What is easy to miss is how much bookkeeping survives the refusal. Slackware writes a plain text record of every installed package into /var/log/packages, and on removal it moves that record aside rather than deleting it. The system knows precisely what is on it and when it arrived. It simply declines to act on that knowledge unattended.

Complete records, no automation. Hold on to that pairing; we come back to it at the bottom of the piece, and the return is not flattering to us.

The Recipe: Declaring What Was Previously Guessed

The obvious complaint about the floor is that the machine knows the dependency exists and still makes you find it by hand. The second answer fixes exactly that, and adds something better while it is there.

Gentoo's Portage and the FreeBSD Ports Collection do the same thing in different accents. A small file per program declares where the source comes from, what it needs, which patches apply, and which optional features may be switched on or off. Gentoo calls those switches USE flags; FreeBSD calls them options. The dependency is no longer inferred after the fact. It is written down before anything is built, by the person who understood the program well enough to package it.

That buys control no binary can offer. Want the database without the graphics libraries, or a daemon compiled with a feature nobody enables by default? Change the declaration, rebuild that one thing, and the tree carries your decision into the next update. You are not off the map; you are still on it, holding the pen.

The cost is hours, and it is not a rounding error. A browser compiled from source is an afternoon you will not get back, and the learning curve is genuine. Anyone who tells you this stage is free has not shipped to a deadline.

Four rungs, and the step below the first 4 · THE TAMING Flatpak, Snap, AppImage, Nix: run across distributions cost: the shared library, so a fix now arrives many times 3 · THE CONVENIENCE pkg, dpkg with apt, rpm with dnf: prebuilt, resolved for you cost: every build option is somebody else's decision 2 · THE RECIPE Portage and Ports: dependencies and options declared, built by you cost: hours, and a genuine learning curve 1 · THE FLOOR Slackware installpkg: no resolution, but a complete written record cost: you find the missing library when the program will not start BELOW · curl into a shell no resolution, no record, no signature, no removal, no audit

The Convenience: Somebody Else's Afternoon

So the third answer keeps the declarations and removes the waiting. Someone else compiles; you install the result. FreeBSD's pkg serves binaries built from the very same ports tree. Debian keeps dpkg for putting packages on disk and apt above it for solving the graph, along the lines its policy document has held for three decades. Red Hat's rpm and dnf do the equivalent. This is where the working world lives, and for good reasons: it is quick, it is transactional, and it scales to an archive of some seventy thousand packages tended by thousands of hands.

The price is quiet enough to miss. Every build option is now somebody else's decision, made once, for everyone. Disagree with it and you are back to compiling, except now you are also the sole maintainer of a package the archive does not expect to see. The recipe still exists. You are simply no longer the one reading it.

The Taming: Giving Up the Shared Library

Then the ground moved. The genuinely hard problem was never resolution inside a distribution; it was software that must run across all of them, each with its own library versions and its own opinions about where things live. Four answers arrived, and they disagree with one another more than their advocates admit.

Flatpak keeps a shared runtime: applications are built against a runtime, that runtime is installed once on the host, and further applications on the same runtime add little to it. Patch the runtime and every application sitting on it is patched at once, which is the old distribution virtue rebuilt one storey up. Snap bundles rather more per application, so ten of them mean ten near-copies of much the same thing. AppImage bundles everything into one file, which is precisely why it runs anywhere and precisely why a library inside it is only ever fixed when the author rebuilds; when a project is abandoned, its bundled libraries are frozen in the state they were abandoned in. Nix takes a different road entirely and stores every build under a hash of its complete dependency tree, which lets incompatible versions sit side by side and makes both upgrades and rollbacks atomic.

Notice the common thread in most of that. The central premise of a distribution, one copy of a library serving every program on the machine, has been quietly abandoned as unworkable across ecosystems. That premise was doing real work: it was the reason a security fix arrived once and covered everything. Give it up and the fix must now arrive many times, from many people, on their schedules rather than yours.

The Step Below the First

Which brings the ladder back down to the ground, and then some.

Ask how you installed your last five development tools. For a good number of readers the honest answer is a line copied off a documentation page: fetch a script over HTTPS, pipe it straight into a shell. Rust's official installer is offered that way. So is Docker's convenience script, and plenty besides. The reasons are not disreputable, either. Upstream will not maintain packages for a dozen distributions with a dozen conventions, and the distribution's version is frequently a year behind what the project actually supports.

But look where the thing sits on the ladder we have just climbed. It does not resolve dependencies, which puts it on the floor with Slackware. It also does not write down what it installed, does not verify a signature before executing, offers no way to remove what it left, and shows up in no audit afterwards. Slackware, declining to guess in 1993, at least kept the receipts. We have arrived at a method that does less than a floppy-disk distribution managed thirty-three years ago, and we run it deliberately, as root, on machines that matter.

What each one actually gives you RESOLVES RECORDS SIGNATURE REMOVAL AUDIT installpkg (1993) no yes no yes yes Ports, Portage yes yes yes yes yes pkg, apt, dnf yes yes yes yes yes Flatpak, Snap, Nix yes yes yes yes varies curl into a shell no no no no no The oldest row on the board beats the newest one in four columns out of five.

The Point

Every step up this ladder solved something real and charged honestly for it: records without automation, control at the price of hours, speed at the price of the build, portability at the price of the shared library. A working engineer can defend any of those on a given Tuesday, and most of us have.

The trade at the bottom is the only one nobody made on purpose. It arrived dressed as a convenience, and it quietly costs the one thing every stage above it kept.

A package manager is not a downloader. It is the memory of what you did to the machine.