- preinstsaves the copy
- unpacks the new files
- removes the obsolete files
- postinstrestores from the copy
Packaging note · dpkg & rpm
Taking a configuration file out of the package is not leaving it alone. It is asking the manager to delete it.
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.
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.
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.
The same cause showed up differently in each manager, which delayed the diagnosis: they looked like two separate faults.
| Manager | What was left | Symptom |
|---|---|---|
| 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.
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.
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.
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:
With no containers around, a namespace is enough to give the scripts a fake root without privileges:
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 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.
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.