Packaging note · dpkg & rpm

The file that disappears

Taking a configuration file out of the package is not leaving it alone. It is asking the manager to delete it.

The setup

A package installs a web server under its own prefix and, with it, its nginx.conf. The operator edits that file: their vhosts live there, their certificates, their site. Every upgrade overwrites it with the stock copy, because the mechanisms that protect configuration — conffiles on Debian, %config(noreplace) on RPM — had only been declared for /etc, and this file does not live there.

The obvious fix: stop shipping it. A file the package does not contain cannot be overwritten. That is correct, and it is not enough.

The trap

dpkg and rpm delete the files a new version no longer ships. That is their job: it is how obsolete binaries and libraries get cleaned up. But they do not distinguish between a stale .so and the configuration an administrator wrote by hand.

So the upgrade that fixes the overwrite is precisely the one that deletes the file. And it happens once, on the transition from the version that still packaged it — which makes it easy to miss until it is in production.

Where the delete lands

Both managers do the same thing in a different order, and the order changes everything. The line marks the moment obsolete files are removed; whatever a hook writes above it does not survive.

dpkg the hook runs late enough ✓
  1. preinstsaves the copy
  2. unpacks the new files
  3. removes the obsolete files
  4. postinstrestores from the copy
rpm %post lands on the wrong side
  1. %presaves the copy
  2. installs the new files
  3. %postwhatever it writes here is lost
  4. erases the old package's files
  5. %posttransrestores from the copy

The two symptoms

The same cause showed up differently in each manager, which delayed the diagnosis: they looked like two separate faults.

ManagerWhat was leftSymptom
deb the stock nginx.conf the server starts and serves nothing: a config with no vhosts
rpm no file at all the server will not even start

On RPM the %post ran before the erase: it found the file still present, decided there was nothing to do, and the erase took it away afterwards.

The fix

Save before the manager can touch anything, restore after it has finished. And on restore, prefer the operator's copy over the stock file: seeding the stock one onto a node that already had a configuration is exactly the overwrite you were trying to avoid.

preinst / %pre cp -a nginx.conf → nginx.conf.pkgsave postinst / %posttrans nginx.conf present? → leave it alone nginx.conf.pkgsave present? → restore (the operator's) nginx.conf.default present? → seed (first install)

On RPM the restore goes in %posttrans, not %post. It is the only hook that runs at the end of the whole transaction, after the erase.

One detail that bites: on Debian the preinst has to be executable. A blanket chmod over the control directory can flatten it back to 0644 after you set it, and then dpkg-deb refuses to build the package at all.

How to verify it

Here is the lesson that actually costs something: inspecting the built package proves nothing. The package can be immaculate — no file, hooks inside — and the upgrade still destroy the configuration. What has to be exercised is the transition from the previous version.

At two levels:

⚠ dpkg --force-script-chrootless runs the scripts against the REAL /, not against the sandbox. It invalidates the test and touches your machine.

With no containers around, a namespace is enough to give the scripts a fake root without privileges:

unshare -r -m sh -c 'mount --bind "$FAKE" "$PREFIX"; ...'

And all four boxes need covering: upgrade and fresh install, crossed with every web server the package carries. Each has its own configuration and its own stock file.

The harness

The script sets up both checks. The first needs dpkg; the second only unshare. Neither touches the machine outside its temporary directory.

verify-package-transition.sh

Reproduces the delete, then exercises the hooks.

Download the script

The rule

When you change which files a package ships, test the upgrade from the previous version. The artefact can be perfect and the transition still destroy data.