Skip to content

The role of the prerm script and the correct way to use it ​

Schematic diagram of the role and correct usage of the prerm script

"When uninstalling a package, the data was lost!" — This is the most common catastrophic mistake beginners make when packaging.

prerm(pre-removal) is a Debian package in before being removed The executed script is responsible for completing cleanup work before the file is deleted. However, because it involves the sensitive operation of "deletion", an incorrect prerm The script may cause:

  • Business data was accidentally deleted

  • The dependency package is corrupted.

  • The upgrade process cannot be rolled back.

This article will provide an in-depth interpretation of prerm all trigger scenarios of it, and provide production-grade security practices.

What is prerm? ​

prerm is in the Debian package Execute script before uninstalling, run before the package's files are deleted from the system. Its responsibilities include:

  • Stop running services (graceful shutdown)

  • Unregister systemd service (disable)

  • Clean up temporary files or cache

  • Yield resources when handling dependency conflicts

📌Naming convention: Script is located at debian/ Under the directory, after packaging, it is installed to /var/lib/dpkg/info/<package>.prerm.

Trigger scenarios for prerm (more complex than postinst) ​

Scenefirst parameterThe second parameterExplanation
Package uninstallationremoveNoneUser-initiated uninstall (keep configuration files)
Version upgradeupgradeNew version numberUpgrade to the new version, and the old version executes prerm
dependency conflictdeconfigureConfigured package namemost complex: The package was forcibly deconfigured due to dependency issues.
Installation failed, rolling backfailed-upgradeOld version numberRoll back to the old version after a failed upgrade

⚠️Key point:deconfigure This is a scenario unique to prerm; many beginners completely ignore it, leading to a chaotic system state in complex dependency scenarios.

Standard template for prerm ​

bash
#!/bin/sh
set -e

case "$1" in
    remove)
        # User uninstall: stop the service, but do not delete data
        ;;
    upgrade)
        # Upgrade to the new version: gracefully stop the old version service
        ;;
    deconfigure)
        # Dependency conflict: package forcibly deconfigured
        # This is the most complex scenario and requires special care.
        ;;
    failed-upgrade)
        # Upgrade failure rollback: do nothing, let the old version recover
        ;;
    *)
        echo "prerm called with unknown argument: $1" >&2
        exit 1
        ;;
esac

exit 0

Hands-on: Writing a prerm for a Go application ​

Continuing from Chapter One's myapp Go service, we need to complete the following operations before uninstalling:

  • Stop the running service

  • Deregister the systemd service (only during a complete uninstall)

  • Retain all business data(Only delete itself, do not delete user data)

Complete prerm script ​

bash
#!/bin/sh
set -e

PKGNAME="$DPKG_MAINTSCRIPT_PACKAGE"

case "$1" in
    remove)
        # User uninstall: stop services but retain all data and configuration files
        # Use deb-systemd-invoke to gracefully stop
        if [ -x /usr/bin/deb-systemd-invoke ]; then
            deb-systemd-invoke stop myapp.service >/dev/null 2>&1 || true
        fi

        # Disable the service (but keep the configuration file)
        if [ -x /usr/bin/deb-systemd-helper ]; then
            deb-systemd-helper disable myapp.service >/dev/null 2>&1 || true
        fi
        ;;

    upgrade)
        # Upgrade to a new version: gracefully shut down the old version
        # Note: Do not disable the service here, otherwise the new version will not be able to be enabled.
        if [ -x /usr/bin/deb-systemd-invoke ]; then
            deb-systemd-invoke stop myapp.service >/dev/null 2>&1 || true
        fi
        ;;

    deconfigure)
        # ⚠️ Forced resolution configuration caused by dependency conflicts
        # This is the trickiest scenario: other packages depend on us, but that package has been uninstalled.

        # Scenario example: myapp depends on mysql, but when mysql needs to be upgraded, the versions are found to be incompatible.
        # At this point, myapp will be deconfigured until the conflict is resolved.

        # Handling method: only stop the service, but do not perform any destructive operations.
        if [ -x /usr/bin/deb-systemd-invoke ]; then
            deb-systemd-invoke stop myapp.service >/dev/null 2>&1 || true
        fi

        # ❌ Error: Do not delete data or disable services in deconfigure
        # Because after deconfigure the package is still installed, just temporarily unavailable.
        ;;

    failed-upgrade)
        # Rollback on upgrade failure: do nothing
        # The system will automatically revert to the old version, and the old version's files are still there.
        # We only need to let the old version of postinst handle the recovery logic.
        ;;

    *)
        echo "prerm called with unknown argument: $1" >&2
        exit 1
        ;;
esac

# Call the code automatically generated by debhelper.
if [ -x /usr/share/debconf/confmodule ]; then
    . /usr/share/debconf/confmodule
fi

exit 0

Key Best Practices ​

1. Distinguish between remove and purge ​

prerm only in remove Stage operation,purge(Thoroughly remove) by postrm Process.

bash
# ❌ Error: Removing configuration files in prerm
remove)
    rm -rf /etc/myapp  # The configuration file has been deleted, and users cannot retain it.

# ✅ Correct: prerm only stops the service and does not delete any files.
remove)
    deb-systemd-invoke stop myapp.service
    # Data is retained and is only deleted during the purge phase of postrm.

Principle:prerm Responsible for "stop",postrm Responsible for "deletion".

2. Use deb-systemd-invoke instead of calling systemctl directly ​

bash
# ❌ Error: Calling systemctl directly
systemctl stop myapp.service

# ✅ Correct: use deb-systemd-invoke
deb-systemd-invoke stop myapp.service

Reason:

  • deb-systemd-invoke Will handle it correctly --no-restart-on-upgrade and other strategies

  • at deconfigure Behavior is safer in the scenario.

  • Avoid errors in container or chroot environments

3. Do not disable the service during upgrade ​

bash
# ❌ Error: Disable the service during upgrade
upgrade)
    systemctl stop myapp.service
    systemctl disable myapp.service  # The new version will not be automatically enabled.
    ;;

# ✅ Correct: stop only, do not disable
upgrade)
    deb-systemd-invoke stop myapp.service
    # The new version will re-enable it in postinst.
    ;;

4. Be especially careful in deconfigure scenarios. ​

deconfigure is the most easily overlooked scenario. It occurs in:

  • Package A depends on Package B

  • Package B is about to be upgraded, but the new version is incompatible with Package A.

  • dpkg decides to first "deconfigure" package A, freeing up the dependency relationship.

  • After package B is upgraded, reconfigure package A.

bash
deconfigure)
    # Only stop the service; do not perform any destructive operations.
    deb-systemd-invoke stop myapp.service >/dev/null 2>&1 || true

    # ❌ Don't do:
    # - Delete any files
    # - Disable systemd services
    # - Delete user
    # - Clean up the database
    ;;

Common Pitfalls and How to Avoid Them ​

Pitfall 1: Deleting user data in prerm ​

bash
# ❌ Catastrophic error
remove)
    userdel myapp
    rm -rf /var/lib/myapp  # All user data is gone!

Correct approach: Data provided by postrm at purge Stage processing,prerm Only responsible for stopping the service.

Trap 2: Use set -e But the command failed. ​

bash
# ❌ If the service is not running, the stop command will fail, causing the installation to be interrupted
set -e
systemctl stop myapp.service

# ✅ Correctly handle failure cases
set -e
systemctl stop myapp.service || true

Pitfall 3: Deleting configuration files in deconfigure ​

bash
# ❌ Error
deconfigure)
    rm -rf /etc/myapp  # Delete the configuration during deconfiguration; when reconfiguring, the configuration is found to be lost.

The correct approach:deconfigure It is a temporary state, the package will be reconfigured afterward, do not perform any irreversible operations.

Pitfall 4: Long blocking during upgrade ​

bash
# ❌ Error: wait for the service to stop completely
upgrade)
    systemctl stop myapp.service --timeout=300  # May block for 5 minutes

The correct approach: Set a reasonable timeout, or handle the migration logic in postinst.

Collaboration between prerm and other scripts ​

Test your prerm ​

Simulated uninstall (keep configuration) ​

bash
# Installation package
sudo dpkg -i myapp_1.0.0.deb

# Uninstall (triggers prerm remove)
sudo dpkg -r myapp

# Check whether the service has stopped
systemctl status myapp  # Should display not found or inactive

Simulated upgrade ​

bash
# Install old version
sudo dpkg -i myapp_0.9.0.deb

# Upgrade (triggers prerm upgrade)
sudo dpkg -i myapp_1.0.0.deb

Simulate deconfigure (complex dependency scenario) ​

bash
# Create two interdependent packages, myapp and mylib
# Triggered when mylib needs to be upgraded but myapp is incompatible

# View the deconfigure log
sudo tail -f /var/log/dpkg.log | grep deconfigure

View the prerm execution log ​

bash
# dpkg detailed log
sudo grep prerm /var/log/dpkg.log

# or use
sudo dpkg --audit

remove vs purge vs deconfigure quick reference table ​

sceneTrigger commandwhat prerm doesWhat postrm does
Uninstall and keep configurationdpkg -rStop service(Do not run)
Completely removedpkg -PStop serviceDelete configuration, logs, and users
Upgradedpkg -i newStop the old versionNew version postinst startup
Dependency conflictAutomatic triggerStop Service (Temporary)Restore during reconfiguration

Security checklist: prerm script checklist ​

Before releasing your Debian package, check against the following checklist prerm:

  • remove The scenario only stops services and does not delete any files.

  • upgrade The scenario only stops the service, not disables it.

  • deconfigure The scenario only stops services and does not perform destructive operations.

  • failed-upgrade The scenario is empty or only performs harmless recovery.

  • Use deb-systemd-invoke rather than directly systemctl

  • All commands have error handling (|| true or conditional judgment)

  • The script uses exit 0 ending

  • Does not depend on other packages that are not installed.

  • At remove Retain all user data in the scene

Summary ​

Key pointsExplanation
prerm Run before file deletionResponsible for "stop" rather than "delete"
Four triggering scenariosremove, upgrade, deconfigure, failed-upgrade
remove vs purgeprerm only handles remove; data cleanup is in postrm
deconfigure most complexOnly stop the service, do not perform destructive operations.
Use deb-systemd-invokeDo not use systemctl directly
Data protection first principleNever delete user data in prerm

Next step ​

In the next chapter, we will explore postrm (post-removal script)—especially purge and remove the boundaries, and how to avoid the fatal problem of the system being unable to reinstall software packages due to script errors.

Next post preview:Chapter 3: The Cleanup Logic of the postrm Script and the purge Trap

最后更新2026/11/07 14:16
如果你觉得这篇文章有帮助,或者想聊聊技术、工作,欢迎通过下面方式联系我:
contact fishfinal