Block Sleep and Shutdown with systemd-inhibit

systemd-inhibit wraps one command in a temporary lock so a sleep, shutdown or idle timer cannot cut it off halfway through. That matters for the job that should never be interrupted: writing removable media, finishing a long export, the thing you find out failed only after the laptop lid closed itself. The examples use systemd 255.4-1ubuntu8.17 from the systemd package.

Allow about ten minutes. You need a shell, the systemd-inhibit command and a working user session connected to systemd-logind. The command does not make a permanent configuration change: it acquires a lock before starting the child process and releases it when that process exits.

1. Check the installed command

Confirm the version and review the available operation names:

$ systemd-inhibit --version
systemd 255 (255.4-1ubuntu8.17)
$ systemd-inhibit --help
systemd-inhibit [OPTIONS...] COMMAND ...

Your version string may differ. The local manpage describes shutdown, sleep and idle, plus the lower-level key and lid handlers. Do not assume an option from a different systemd release is available here.

Checkpoint: If the command is missing, install or enable the package using your distribution's normal package process. Do not substitute an unrelated desktop power-management command.

2. Protect one command with the default lock

Put systemd-inhibit before the command that must finish. This example protects a one-second test process:

$ systemd-inhibit \
    --what=shutdown:sleep:idle \
    --who='media export' \
    --why='finishing the export' \
    sleep 1

The lock exists only while the child is running.

Safety boundary: An inhibitor is not a backup, a transaction or a guarantee that hardware will stay powered. It does not prevent a forced power cut, a kernel failure or every possible administrative action. Keep the protected operation bounded and give it its own recovery plan.

3. Use a real operation and preserve its exit status

A practical pattern is to protect a command that already has a clear input and output. This example writes a new archive and leaves the original directory untouched:

set -e
systemd-inhibit \
    --what=sleep:shutdown \
    --who='nightly archive' \
    --why='writing the archive' \
    tar -czf /path/to/new-archive.tar.gz /path/to/source-directory

Replace both paths with real paths before running it, and check free space first because the output archive can be large. Do not point the output at an existing important archive unless overwriting it is deliberate and recoverable.

systemd-inhibit returns the exit status of the executed program, so a successful wrapper status means tar succeeded, not that the source now matches your wider retention policy. In a shell script, capture or test the wrapper's status immediately, before another command changes $?.

Checkpoint: Try a harmless sleep 1 first. If the wrapper reports Failed to inhibit: Access denied, the session cannot acquire this lock. Do not hide that error with || true; investigate the session and logind policy, or decide the operation must stop when protection is unavailable. Elevated privileges are not a normal prerequisite, and sudo may just change which session can create the lock rather than fix the underlying policy.

4. Inspect active inhibitor locks

Use --list to list active locks instead of starting a child command:

$ systemd-inhibit --no-pager --no-legend --list
ModemManager                 0 root 2496533 ModemManager    sleep    ModemManager needs to reset devices                       delay
NetworkManager               0 root 996     NetworkManager  sleep    NetworkManager needs to turn off networks                 delay
UPower                       0 root 3157839 upowerd           sleep    Pause device polling                                      delay

The exact rows depend on the machine and change as services start or stop. The columns identify the user ID, process ID, program, inhibited operation, reason and mode. Output may be paged unless you pass --no-pager; --no-legend removes headings and the footer, which suits scripts, but do not parse this human-readable layout as a stable machine interface without testing it against your systemd version.

A lock belonging to a process that has already exited should disappear on its own. If a lock remains while a long-running operation is active, find that process and let it finish normally. Killing it releases the lock but can leave its work incomplete, so use the application's own cancellation and recovery procedure first.

5. Choose block or delay deliberately

The default mode is block. It prohibits the requested operations without a time limit, and only privileged users may override it. Use it when an operation must either finish or be stopped by an administrator who understands the consequences.

--mode=delay asks logind to delay a sleep or shutdown only for a limited period, set in logind.conf. The manpage restricts delay mode to sleep and shutdown; it does not apply to idle or the key and lid handler operations.

$ systemd-inhibit \
    --what=sleep:shutdown \
    --mode=delay \
    --who='short export' \
    --why='allowing the export to finish' \
    /path/to/export-command

Do not pick delay mode merely because it sounds less intrusive. A long export can outlive the configured limit and get interrupted anyway. If you need an unbounded lock, use the default block mode and make the command's duration and cancellation behaviour clear to the people operating the machine.

Common traps

There is no persistent lock file to delete and no undo command to run. Recovery is simply letting the child exit, or stopping it through its documented cancellation path. Once it exits, run systemd-inhibit --no-pager --no-legend --list again if you need to confirm the transient lock has gone.

Done means