PostgreSQLXII · WAL, Checkpoints and Crash RecoveryWAL
Shutdown modes and their recovery consequences
What you'll learn
- Choose the correct shutdown mode for a given situation
- Explain why smart shutdown can make a cluster unavailable without completing
- Predict whether a given stop will require recovery on restart
- Configure systemd so that a host reboot shuts PostgreSQL down correctly
Prerequisites
Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27
Three modes, and the one that sounds most considerate is the one most likely to cause an outage.
$ pg_ctl --helpShutdown modes are:
smart quit after all clients have disconnected
fast quit directly, with proper shutdown (default)
immediate quit without complete shutdown; will lead to recovery on restart| Mode | Signal | Existing sessions | Clean shutdown? | Recovery on restart? |
|---|---|---|---|---|
smart | SIGTERM | Waits for them to leave | Yes | No |
fast | SIGINT | Disconnected, transactions rolled back | Yes | No |
immediate | SIGQUIT | Killed | No | Yes |
Fast: the default and usually the answer
$ pg_ctl -D $PGDATA -m fast -w stopwaiting for server to shut down....done
elapsed: 119 ms
-- on restart:
LOG: database system was shut down at 2026-08-27 21:13:23 UTC
LOG: database system is ready to accept connections
-- the uncommitted rows:
SELECT count(*) FROM crashtest WHERE id > 2000000; -> 0119 milliseconds. The session was disconnected, its uncommitted work discarded — correctly, since it was never committed — and the shutdown completed cleanly, so the restart needed no recovery.
fast is the right choice for planned maintenance, deployments,
failovers and almost everything else.
Smart: the trap
$ pg_ctl -D $PGDATA -m smart -w -t 10 stoppg_ctl: server does not shut down
HINT: The "-m fast" option immediately disconnects sessions rather than
waiting for session-initiated disconnection.
elapsed: 10 s, and the server was still running$ psql -U postgres -c "SELECT 'still up'"psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432"
failed: FATAL: the database system is shutting downImmediate: a deliberate crash
$ pg_ctl -D $PGDATA -m immediate -w stopwaiting for server to shut down....done
-- on restart:
LOG: database system was interrupted; last known up at 2026-08-27 21:14:24 UTC
LOG: database system was not properly shut down; automatic recovery in progress
LOG: redo is not required
LOG: checkpoint complete: wrote 0 buffers (0.0%) ... distance=0 kB, estimate=0 kB
LOG: database system is ready to accept connectionsThe was interrupted and not properly shut down lines are identical
to the SIGKILL case in lesson XII-06. Immediate shutdown is a crash that
you asked for.
redo is not required appears here only because a checkpoint had
completed moments earlier and nothing had been written since. On a busy
cluster the same command produces a full redo. The mode does not
choose how much recovery is needed; the distance since the last
checkpoint does.
Legitimate uses: a hung cluster that will not respond to fast, and a
cluster you are about to discard. Not legitimate: as a faster way to
stop a healthy server, because the time saved on the way down is paid
back with interest on the way up.
Systemd, and the reboot you did not think about
A host reboot sends SIGTERM to services, which for PostgreSQL is
smart mode. With a pooler holding connections, that stops nothing;
systemd then waits for TimeoutStopSec — 90 seconds by default — and
sends SIGKILL, which is an unclean crash.
So a routine reboot produces a 90-second hang followed by a crash, on a cluster that would have stopped cleanly in 119 milliseconds.
The packaged units on major distributions handle this, and it is worth confirming rather than assuming:
systemctl cat postgresql@18-main | grep -E 'KillSignal|TimeoutStopSec|ExecStop'
What you want to see is either an ExecStop invoking pg_ctl with
-m fast, or KillSignal=SIGINT, which is the signal fast mode uses.
[Service]
KillSignal=SIGINT
TimeoutStopSec=300
KillMode=mixed
TimeoutStopSec should exceed the time a fast shutdown genuinely takes
on your cluster — the shutdown checkpoint has to write every dirty
buffer, which on a large shared_buffers is not instant.
What to take from this
fastis the default and the right answer almost always. Measured at 119 ms.smartwaits indefinitely and refuses new connections. One idle session blocked it for its full timeout.immediateis a deliberate crash. The restart log is identical to a SIGKILL.- A host reboot sends
SIGTERM, which is smart mode. Check your systemd unit uses-m fastorKillSignal=SIGINT. CHECKPOINTbefore stopping, so the shutdown checkpoint is small.- Confirm with
pg_controldata:Database cluster state: shut down.
Cross-course references
- Linux for Production Sysadmins — Part VII (systemd) covers
TimeoutStopSecand what expires into a kill, which is how a smart shutdown quietly becomes a crash. - Docker & Containers — Part VI (Container lifecycle) covers
docker stop’s grace period, which is the same trap with a different default. - Kubernetes for Production Sysadmins — Part X (Pod termination and
signals) covers
terminationGracePeriodSeconds, which is where this goes wrong on a cluster nobody tuned.
Quiz
Knowledge check · 6 questions
Q1. An operator issues a smart shutdown on a cluster fronted by a connection pooler. What happens?
Q2. A host reboot leaves PostgreSQL performing crash recovery on every start, despite the reboot being planned. What is the most likely cause?
Q3. A fast shutdown on a cluster with 64 GB of shared_buffers is taking several minutes. Why, and what would have helped?
Q4. Which are true of an immediate shutdown? Select all that apply.
Q5. After stopping a cluster, pg_controldata reporting 'Database cluster state: shut down' is the proof that the stop was clean.
Q6. Write the sequence you would follow to stop a large production cluster for maintenance, and say why each step is there.
Passing score: 75%. Answers are checked in this browser.