Home / Alt manpages / deb-triggers(5)

  • deb-triggers(5)
  • File format
  • linux

Declare Debian Package Triggers Without Stalling Upgrades

You will finish with a small Debian package skeleton containing a valid DEBIAN/triggers file, and a clear rule for choosing between interest-noawait and its waiting variants. The examples match dpkg 1.22.6, provided here by package version dpkg-dev 1.22.6ubuntu6.6.

Allow about fifteen minutes. You need a shell, dpkg-deb from the installed dpkg suite, and a writable temporary directory. This guide builds and inspects an archive only. It does not install it, activate a trigger on the host, or change package state, so the commands normally need no elevated privileges.

1. Decide which package owns the event

A trigger is a named hand-off between packages. The package that receives and processes the event declares an interest. The package whose state change causes the event declares an activation. For an explicit trigger, both packages must agree on the exact name.

Use a name that describes the interface and is unlikely to collide with another package. The trigger name is not a shell command and should not contain whitespace. In this example, a package that maintains a cache will process example-cache-refresh when another package changes:

$ TRIGGER_NAME='example-cache-refresh'
$ printf '%s\n' "$TRIGGER_NAME"
example-cache-refresh

Checkpoint: write down the interested package, the activating package, and the trigger name before editing either package. A spelling mismatch creates two unrelated interfaces.

2. Add the interested package's declaration

In a source package, put the declaration in debian/triggers. When one source builds several binary packages, use debian/BINARY-PACKAGE.triggers to target one binary package. During package construction, the file becomes DEBIAN/triggers in that binary package's control archive.

For a cache refresh that is useful but not required before the activating package can be configured, use the no-wait form:

interest-noawait example-cache-refresh

Whitespace at the start or end of a line is ignored. Text after the first # is a comment, and blank lines are ignored. Keep one directive per line. Unknown directives are errors and prevent installation.

The ordinary interest form permits the activation to put the triggering package into triggers-awaited. interest-await is the explicit spelling of that waiting behaviour. interest-noawait prevents that wait even when the activating side asks for one. Choose no-wait only when delaying the trigger processing cannot make the activating package unusable or leave a required invariant broken.

Checkpoint: inspect the file as plain text. There should be one supported directive and the exact trigger name:

$ sed -n 'l' debian/triggers
interest-noawait example-cache-refresh$

3. Add activation to the changing package

In the package whose installation or removal represents the event, add the matching activation directive. The activation is associated with package state changes, not with an arbitrary file write during normal operation:

activate-noawait example-cache-refresh

dpkg activates this trigger when the package is unpacked, configured, removed, purged or deconfigured. If the package disappears while another package is being unpacked, dpkg activates it when that disappearance is noticed near the end of the unpack. Trigger processing itself does not cause another activation.

The activation forms are parallel to the interest forms. activate allows the requested waiting mode, activate-await requests waiting, and activate-noawait never puts the triggering package into triggers-awaited. The interested package still has the final say: interest-noawait forces no-wait for activations it receives.

Do not assume that activate-await always creates a wait. It only does so when the interested package uses interest or interest-await. This pairing is the most common source of misleading package-state checks.

4. Build a harmless archive and inspect its control data

You can test the file without installing anything. A minimal binary package needs a control file and the triggers file under a temporary package tree:

$ workdir=$(mktemp -d)
$ mkdir -p "$workdir/pkg/DEBIAN"
$ printf '%s\n' \
  'Package: example-cache' \
  'Version: 1.0' \
  'Architecture: all' \
  'Description: Trigger declaration test package' \
  > "$workdir/pkg/DEBIAN/control"
$ printf '%s\n' 'interest-noawait example-cache-refresh' > "$workdir/pkg/DEBIAN/triggers"
$ dpkg-deb --build "$workdir/pkg" "$workdir/example-cache.deb"
dpkg-deb: building package 'example-cache' in '.../example-cache.deb'.
$ dpkg-deb --control "$workdir/example-cache.deb" "$workdir/control"
$ sed -n 'l' "$workdir/control/triggers"
interest-noawait example-cache-refresh$

The temporary directory is not package state. Review the extracted control data, then remove it when you are finished:

$ rm -rf "$workdir"

That final command is destructive for the temporary directory only. If you need the archive for later review, copy it somewhere deliberate before removing the directory. Never substitute a broad path for $workdir.

5. Implement trigger handling in the maintainer script

When an interested package has a pending trigger, dpkg runs its postinst with the action triggered and a space-separated list of trigger names as the second argument. A shell maintainer script should handle the names independently because several activations can be delivered together:

case "$1" in
  triggered)
    case " $2 " in
      *" example-cache-refresh "*) refresh-cache ;;
    esac
    ;;
esac

The processing must be safe to repeat. dpkg may aggregate repeated activation, and the package may need to recover work that happened while it was not configured. Treat the trigger list as an optimisation hint, not as permission to skip necessary initial setup.

Do not put a trigger name in a package's file merely because a daemon should react to every runtime event. Debian package triggers are for events around package installation and state changes. Runtime notification needs a separate mechanism.

6. Diagnose waiting and pending states

After a real package operation, inspect package status with dpkg rather than assuming that a successful unpack means every trigger has completed:

$ dpkg-query -W -f='${Package} ${Status}\n' example-cache
example-cache install ok installed

During processing, an interested package can be triggers-pending; a triggering package can be triggers-awaited. Pending trigger work is normally attempted at the end of the relevant dpkg run. If the triggered maintainer script fails, the package can become config-failed, and dpkg will not silently retry it forever.

If a package remains in one of these states, first read the maintainer-script error and package status. Do not run a blind reinstall or delete dpkg's database files. Re-run the normal package configuration procedure only after correcting the script or dependency problem, and keep a copy of the diagnostic output for recovery.

7. Check compatibility before publishing

The -noawait variants have been supported since dpkg 1.16.1. The -await aliases have been supported since dpkg 1.17.21. Older dpkg versions reject these directives, so check the oldest supported distribution before shipping them:

$ dpkg-query -W -f='${Package} ${Version}\n' dpkg
dpkg 1.22.6ubuntu6.6

For a package family with older targets, either require a sufficiently new dpkg or use the older directive vocabulary supported by those targets. Record that decision in packaging documentation; an unknown trigger directive is an installation failure, not a harmless compatibility downgrade.

Done means

  • The interested and activating packages use the same whitespace-free trigger name.
  • Each directive is in the correct source-package or binary-package triggers file.
  • You chose no-wait only where deferred processing is safe.
  • The archive builds and contains the expected DEBIAN/triggers data.
  • The postinst triggered path handles names separately and can be run repeatedly.
  • You checked the minimum supported dpkg version and retained a recovery path for script failures.