The Ultimate Veto
You know that systemctl disable nginx prevents a service from starting at boot. But "disabled" is not the same as "impossible to start." A disabled service can still be started manually with systemctl start nginx, or started automatically if another service declares Requires=nginx.service or Wants=nginx.service.
Sometimes you need an absolute guarantee that a service never runs, regardless of who asks. Common scenarios:
- Preventing
bluetooth.servicefrom running on a hardened server where Bluetooth is a security risk. - Preventing a conflicting service (like
firewalldvsufw) from being accidentally started. - Ensuring a deprecated service is never activated during a migration period.
The Solution: Masking
systemctl mask <unit> creates a symbolic link from the unit's location to /dev/null. When anything afterward — a user, another service, a package post-install script — tries to start that unit, systemd follows the symlink, reads /dev/null, finds an empty file, and refuses to start it, reporting an explicit "unit is masked" error. systemctl unmask <unit> reverses it, removing the symlink and restoring normal behavior.
The distinction between disable and mask maps to a real operational decision: disable means "don't start this automatically, but it's available if needed." mask means "this service must not run on this system under any circumstances."
A common interview question is: "What's the difference between systemctl stop, systemctl disable, and systemctl mask?" The precise answer: stop halts the currently running instance but leaves it enabled for future starts. disable removes the boot-time symlink so it won't start automatically, but it can still be started manually or by dependencies. mask creates a /dev/null symlink that makes the unit impossible to start by any means until unmasked.