Start and Stop a Daemon Safely with start-stop-daemon

start-stop-daemon starts and stops one daemon precisely, so an init script never kills the wrong process by accident. That precision comes from matching options you have to get right yourself. The examples below follow the installed dpkg start-stop-daemon 1.22.6 on this machine.

Allow about fifteen minutes. You need a shell and a daemon with a documented executable path, service user and, ideally, a pidfile. Starting or stopping a real service normally needs elevated privileges; the read-only checks and dry runs below do not.

1. Confirm the installed command

Check the binary and package version before you trust any example, this one included:

$ command -v start-stop-daemon
/usr/sbin/start-stop-daemon
$ start-stop-daemon --version
start-stop-daemon 1.22.6 for Debian
$ dpkg-query -W -f='${Package} ${Version}\n' dpkg
dpkg 1.22.6

The command has four main actions: --start, --stop, --status and --help. Matching options decide which existing process counts as "the daemon". Starting adds --exec or --startas, and anything after -- is passed straight to the program.

Checkpoint: write down the real executable, service account and pidfile path for your daemon. Do not substitute a process name from memory.

2. Build a precise match

For a real service, combine more than one match restriction. A typical match uses the executable, user and pidfile together:

$ sudo start-stop-daemon --start --test --oknodo \
    --exec /usr/sbin/exampled \
    --user example \
    --pidfile /run/exampled.pid \
    --startas /usr/sbin/exampled -- --foreground

--test prints the action it would take and changes nothing, which makes it the safest way to catch a misspelled path or a match that is broader than you intended. The -- before --foreground separates start-stop-daemon's own options from the daemon's arguments.

3. Start only after the dry run is right

Repeat the command without --test once its proposed action looks correct. This actually changes service state, so use whatever account and maintenance controls your host requires:

$ sudo start-stop-daemon --start --oknodo \
    --exec /usr/sbin/exampled \
    --user example \
    --pidfile /run/exampled.pid \
    --startas /usr/sbin/exampled -- --foreground

--start checks for a matching process first, and if one already exists, no second instance gets started. Without --oknodo that no-op returns status 1; with it, an already-running daemon counts as success, which an init script usually finds easier to handle.

Verify the result using the same match:

$ sudo start-stop-daemon --status \
    --exec /usr/sbin/exampled \
    --user example \
    --pidfile /run/exampled.pid
$ printf 'status: %s\n' "$?"
status: 0

For --status, status 0 means the program is running, status 1 means it is not but the pidfile exists, status 3 means it is not running at all, and status 4 means the status could not be determined. Treat a stale pidfile as something to investigate, never as permission to kill a process by number.

4. Stop it with a bounded wait

Stopping sends SIGTERM by default. Start with a dry run, then repeat with the same exact match:

$ sudo start-stop-daemon --stop --test --oknodo \
    --exec /usr/sbin/exampled \
    --user example \
    --pidfile /run/exampled.pid \
    --retry 5

The timeout form --retry 5 means TERM, wait up to five seconds, then KILL, then wait another five. KILL cannot be caught by the daemon, so it can lose in-memory work; use it only after giving the normal shutdown path a fair interval.

Once the dry run looks right, stop the service for real:

$ sudo start-stop-daemon --stop --oknodo \
    --exec /usr/sbin/exampled \
    --user example \
    --pidfile /run/exampled.pid \
    --retry 5
$ sudo start-stop-daemon --status \
    --exec /usr/sbin/exampled \
    --user example \
    --pidfile /run/exampled.pid
$ printf 'status: %s\n' "$?"
status: 3

If the daemon leaves its pidfile behind, add --remove-pidfile to the stop command, but only once you have checked the file really is the one you intend to remove. It deletes the file after termination. Recovery without it is simple: remove the stale pidfile by hand once you have confirmed no matching process remains, or let the daemon's own service wrapper handle the cleanup.

5. Choose a custom retry schedule only when needed

Some daemons need more time or a different escalation. A schedule names signals and waits explicitly:

$ sudo start-stop-daemon --stop --oknodo \
    --exec /usr/sbin/exampled \
    --user example \
    --pidfile /run/exampled.pid \
    --retry=TERM/30/KILL/5

Each item is separated by a slash, so this schedule sends TERM, waits 30 seconds, sends KILL, then waits another five. If the schedule runs out while a process is still alive, the command returns status 2. A retry schedule overrides --signal, so do not add both expecting them to combine.

6. Read exit statuses in scripts

For start and stop, status 0 means the requested action happened, or that --oknodo waved through an intentional no-op. Status 1 means nothing happened because the match was already in the requested state, status 2 is an unfinished retry schedule, and status 3 is another kind of error. Keep these cases visible in a wrapper instead of collapsing every non-zero result into "service failed":

if start-stop-daemon --start --oknodo \
    --exec /usr/sbin/exampled --user example \
    --pidfile /run/exampled.pid --startas /usr/sbin/exampled; then
    printf '%s\n' 'exampled is starting or already running'
else
    rc=$?
    printf 'start-stop-daemon failed: %s\n' "$rc" >&2
    exit "$rc"
fi

For long-lived children, always use a pidfile. Without one, stop scans matching processes much like killall, and a daemon's own children can get swept up too. A pidfile narrows the operation, but only when its lifecycle and permissions are actually trustworthy.

Done means