Skip to main content
RunBook Academy

Docker & ContainersXXX Β· Host MaintenanceReboots

Planned reboot β€” what happens to containers, in order

Intermediate⏱ ~24 mindockersystemd

What you'll learn

  • Trace the shutdown sequence from `systemctl reboot` to container SIGKILL
  • Tune `shutdown-timeout` and `default-stop-timeout` against the systemd stop timeout
  • Predict which containers come back on boot and which do not
  • Verify the host after a reboot rather than assuming

Prerequisites

Verified against Docker Engine 29.x Β· Docker Engine 28.x Β· Docker Compose 2.x Β· containerd 2.x Β· runc 1.2.x Β· BuildKit 0.20+ Β· Linux kernel 5.15+ Β· Ubuntu 24.04 LTS Β· Debian 12 (Bookworm) Β· 2026-08-11

Not yet marked complete on this device.

The upgrades part spent a lot of effort on containers surviving a daemon restart. None of it applies here. A reboot takes the kernel down, and every container is a process on that kernel.

So the question for a reboot is not β€œdo containers survive” β€” they do not β€” but β€œdo they shut down cleanly and come back correctly”. Both answers are decided by configuration you can check in advance.

The shutdown sequence

The timeouts, and why the defaults bite

There is no docker info field for the daemon’s shutdown budget, which is part of why it goes unnoticed. Read it from the configuration instead β€” an absent key means the default:

Read-only / Safethe three budgets
$ grep -E 'shutdown-timeout|default-stop-timeout' /etc/docker/daemon.json ; systemctl show docker.service -p TimeoutStopUSec ; docker inspect --format '{{.Name}} {{.Config.StopTimeout}}' $(docker ps -q)
TimeoutStopUSec=1min 30s
/web <nil>
/postgres 60

Illustrative output

No grep output at all means neither daemon key is set, so the daemon is using shutdown-timeout 15 and default-stop-timeout 10. A container printing <nil> for Config.StopTimeout has no per-container override and inherits the daemon default; /postgres above was created with --stop-timeout 60.

Consider a Postgres container configured with --stop-timeout 60, because a checkpoint on a large database takes that long:

  • Step 4 gives that container its 60 seconds. Correct.
  • Step 5 gives the daemon 15 seconds total. After 15 seconds dockerd stops waiting and exits.
  • The container is SIGKILLed mid-checkpoint.

The per-container timeout is capped in practice by the daemon’s overall shutdown-timeout, and the default is 15 seconds. A container asking for more than that does not get it during a host shutdown, even though docker stop -t 60 by hand works perfectly.

Configuration change/etc/docker/daemon.json
{
"shutdown-timeout": 90,
"default-stop-timeout": 30
}

Then raise the systemd unit budget above the daemon’s, so systemd is the last thing to lose patience rather than the first:

Configuration change/etc/systemd/system/docker.service.d/timeout.conf
[Service]
TimeoutStopSec=120
Configuration changeapply
sudo systemctl daemon-reload
systemctl show docker.service -p TimeoutStopUSec

# Validate the daemon config before restarting into it
sudo dockerd --validate --config-file /etc/docker/daemon.json
sudo systemctl restart docker

# The daemon logs its effective configuration at startup
journalctl -u docker --since '2 minutes ago' --no-pager | head -20

The ordering rule: container stop timeout ≀ daemon shutdown-timeout ≀ systemd TimeoutStopSec. Any inversion means something gets killed by a layer that does not know what it is interrupting.

What comes back on boot

Restart policy decides, and the four policies behave differently:

PolicyAfter a host reboot
noDoes not start. Stays exited.
on-failureDoes not start. The policy responds to exit codes, not to daemon startup.
alwaysStarts.
unless-stoppedStarts, unless it was manually stopped before the shutdown.

The on-failure row is the trap. It reads like a reasonable production setting and it means the container will not come back from a reboot. Docker’s documentation is explicit that on-failure does not restart the container if the daemon restarts.

The difference between always and unless-stopped matters exactly once per reboot: a container you deliberately stopped last week comes back with always and stays stopped with unless-stopped. For a host you reboot for maintenance, unless-stopped respects your intent and always overrides it.

Read-only / Safepre-reboot policy audit
docker ps -a --format '{{.Names}}' | xargs -r -I{} docker inspect     --format '{{.Name}} {{.State.Status}} {{.HostConfig.RestartPolicy.Name}}' {}
Read-only / Safeaudit result
$ docker ps -a --format '{{.Names}}' | xargs -r -I{} docker inspect --format '{{.Name}} {{.State.Status}} {{.HostConfig.RestartPolicy.Name}}' {}
/web        running   unless-stopped
/api        running   unless-stopped
/worker     running   on-failure
/postgres   running   always
/debug-box  running   no

Illustrative output

worker and debug-box will be gone after the reboot. debug-box is probably fine β€” it is a debugging container. worker is a production service with the wrong policy, and this audit is how you find it while you can still fix it.

The reboot procedure

Service impact possibleplanned reboot
#!/usr/bin/env bash
set -euo pipefail

# --- Before
uname -r
cat /var/run/reboot-required.pkgs 2>/dev/null || echo 'no reboot flagged'
docker ps --format '{{.Names}}' | sort > /tmp/running.before
docker ps -a --format '{{.Names}}' | xargs -r -I{} docker inspect --format '{{.Name}} {{.HostConfig.RestartPolicy.Name}}' {} > /tmp/policies.before
cat /tmp/policies.before

# --- Optional: stop the stack in dependency order rather than letting
#     systemd do it. Gives the application a clean shutdown it controls.
# docker compose -f /srv/app/compose.yaml stop --timeout 60

sudo systemctl reboot

Explicitly stopping a Compose stack before the reboot is worth it when services depend on each other. Compose stops them in reverse dependency order, so an application shuts down before the database it writes to. The daemon’s own shutdown has no such ordering β€” it stops containers concurrently, which means a database can go away while an application is still writing to it.

Read-only / Safeafter the reboot
uname -r
systemctl is-system-running
systemctl is-active docker containerd

docker ps --format '{{.Names}}' | sort > /tmp/running.after
diff /tmp/running.before /tmp/running.after && echo 'PASS: same containers running' || echo 'FAIL: container set changed - see diff above'

docker ps --filter health=unhealthy --format '{{.Names}}'

Knowledge check

Knowledge check Β· 4 questions

  1. Q1. A container is configured with `--stop-timeout 60` but the daemon still has the default `shutdown-timeout`. What happens to it during a host reboot?

  2. Q2. Which restart policy leaves a container stopped after a host reboot even though it was running before?

  3. Q3. Which statements about a host reboot are correct? Select all that apply.

  4. Q4. A container SIGKILLed during shutdown often shows no visible symptom, because the application performs crash recovery on the next boot and starts normally.

Passing score: 75%. Answers are checked in this browser.