Enable Units from Maintainer Scripts with deb-systemd-helper
Your package enables a service on install, and an admin who turned it off finds it back on after every upgrade. deb-systemd-helper exists to stop exactly that. By the end you will have a maintainer-script pattern that records a unit's enabled state without starting anything, plus a way to inspect the result and redirect operations into a chroot. It is a packaging helper, not a replacement for an administrator's systemctl command.
The route
Jump straight to the step you need, or tick off Done means at the end.
Before you start
Allow about 15 minutes. You need a Debian or Ubuntu package build or test environment, the init-system-helpers package, and a unit file your package actually ships. The installed man page here comes from init-system-helpers 1.66ubuntu1, dated 6 December 2023. Check behaviour outside that version against the target distribution.
Warning
Stop here if you are an administrator trying to enable a service on a running host. The man page says this helper is for maintainer scripts and is not intended for interactive use. Use systemctl when systemd is running, or leave the enabled state alone on a machine that does not run systemd.
1. Choose the maintainer-script action
Call the helper with an action, then one or more unit file names. A typical package postinst action is:
deb-systemd-helper enable example-worker.service
The helper's enable is deliberately not an unconditional systemctl enable. It happens only once, when the package is first installed. The first enable creates a state file, and purge removes that state. That lets a package upgrade leave the administrator's later choice alone.
Use the matching lifecycle actions in the maintainer scripts your package needs:
enablerecords the initial enabled state.disablerecords that the unit should not be enabled.purgeremoves the helper's package state.reenableapplies the helper's re-enable operation.maskandunmaskpreserve the enabled or disabled state around a mask.
There is no start or stop action, so do not make one up. This helper manages enablement state. Starting or stopping a service is a separate operational decision.
2. Put the call in the package workflow
Maintainer scripts run with the privileges and environment the package manager supplies. Tie the unit name to a file the package ships, and quote a variable if your packaging code uses one:
unit_file='example-worker.service'
deb-systemd-helper enable "$unit_file"
That is ordinary shell, but it belongs in a maintainer-script test or a package build workflow, not a terminal on a production machine. Follow the package's normal error-handling policy too. Do not hide a failed helper call just to make an upgrade look successful.
Safety warning
purge changes and removes recorded state. Use it only on the package removal path, after checking the package really owns the unit. It is not an undo button for an arbitrary administrator change.
3. Query state without misreading it
The helper has several Debian-specific queries:
is-enabledchecks the recorded enablement state.was-enabledreports whether a service was enabled before an updated service file was installed. Handy when a script must preserve a previous choice.debian-installedsucceeds when the state file for at least one supplied unit exists.
deb-systemd-helper is-enabled example-worker.service
deb-systemd-helper was-enabled example-worker.service
deb-systemd-helper debian-installed example-worker.service
These answer mainly through their exit status. In a shell test, capture that status immediately:
if deb-systemd-helper is-enabled example-worker.service; then
echo 'recorded as enabled'
else
echo 'not recorded as enabled'
fi
Checkpoint
A non-zero result does not mean the service is stopped, broken or absent. It only means the recorded-state condition you asked about was not met. Do not turn this helper into a service-health check.
4. Test a package root safely
When a maintainer script works on a chroot, set DPKG_ROOT to that directory. The helper then operates under that root instead of the host root:
DPKG_ROOT=/srv/package-root deb-systemd-helper update-state
update-state is a Debian-specific action. It removes obsolete entries and adds entries for new service files without enabling them. That suits synchronising the helper's state after package files change, but it does not activate a service.
Warning
Use an explicit, prepared test root such as /srv/package-root, and check the directory before you run the command. Do not point DPKG_ROOT at / to make a test feel realistic. If a test changes state, remove the disposable root with your normal package-test cleanup rather than repairing a production host by hand.
5. Turn on diagnostics only when investigating
For a maintainer-script or package-manager bug, export _DEB_SYSTEMD_HELPER_DEBUG=1. Debug messages go to standard error, so they stay visible in a dpkg run:
_DEB_SYSTEMD_HELPER_DEBUG=1 deb-systemd-helper is-enabled example-worker.service
Scope this to the failing test or package operation. It is evidence, not service configuration. The installed program also refuses normal interactive invocation, another hint that its interface is built for package scripts.
Common traps
- Expecting
enableon every upgrade. Its one-time behaviour protects later administrator choices. - Starting a daemon with it. Enablement state and running state are different concerns.
- Treating
is-enabledas a health check. It reports recorded state, not process health. - Testing a chroot without
DPKG_ROOT. That risks changing the host's package state. - Passing a unit the package does not own. Keep unit names aligned with the files the package ships.
Done means
- Documented action. The maintainer script uses one, against a real shipped unit file.
- Exit status, not health. Enablement checks read the exit status and are not mistaken for service-health checks.
- Disposable root. State-changing tests go through
DPKG_ROOT. - Purge in its place.
purgeappears only in the package purge path. - Debug on demand. Debug output is enabled only while collecting evidence for a failure.