Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Postgres By James Joyner IV · · 8 min read Last reviewed Jul 2026

PostgreSQL Error Guide: 'the database system is starting up' — Fix

Quick answer

Fix 'the database system is starting up' in Postgres: understand crash recovery, WAL replay, standby startup, and slow restarts, with real diagnostics to tell a normal wait from a stuck server.

Part of the PostgreSQL Database Errors hub
  • #postgres
  • #database
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this Postgres error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

PostgreSQL returns this when a client tries to connect while the server is still initializing and not yet ready to accept queries:

FATAL:  the database system is starting up

It is FATAL for the connection but often not an error at all — it’s the expected response during startup, crash recovery, or WAL replay on a standby. The real question is whether startup is progressing normally or is stuck, and the fix depends entirely on that distinction.

Symptoms

  • Clients and health checks fail to connect right after a restart, crash, or failover, then recover on their own once startup completes.
  • The message persists for minutes (or longer) after an unclean shutdown while crash recovery replays WAL.
  • A hot standby returns it during initial startup before it reaches a consistent recovery point.
  • Connection-pool warm-up floods logs with this message during a rolling restart.
  • On a large/busy instance, recovery after a crash takes far longer than a clean restart.

Common Root Causes

  • Normal startup — the server is simply booting and hasn’t opened for connections yet (seconds on a healthy instance).
  • Crash recovery / WAL replay — after an unclean shutdown (OOM kill, power loss, kill -9), Postgres replays the write-ahead log to reach a consistent state before accepting connections. This can take a long time with a large max_wal_size/checkpoint distance.
  • Standby reaching consistency — a replica replays WAL from the primary/archive before it can serve reads.
  • Slow storage replaying WAL, extending recovery time.
  • A restore/PITR replaying archived WAL to a target.
  • Stuck startup — waiting on a missing WAL segment, an unreachable archive (restore_command hanging), or corrupt WAL, so it never finishes.

Diagnostic Workflow

The single most important step is to read the server log — it tells you whether recovery is progressing or stuck:

# find the log destination
psql -c "SHOW log_directory;" 2>/dev/null || true
# tail the server log (path varies by distro)
tail -f /var/log/postgresql/postgresql-16-main.log

Look for recovery progress lines like redo starts at, recovering ... 0000000100000042..., and finally database system is ready to accept connections. Advancing LSNs mean it’s working; a repeated wait on one segment means it’s stuck.

Confirm the postmaster is actually running and in recovery:

ps aux | grep -E 'postgres.*(startup|recovering)'
pg_ctl status -D /var/lib/postgresql/16/main

Once you CAN connect (or from a standby that reaches read state), check recovery status and progress:

SELECT pg_is_in_recovery();
SELECT pg_last_wal_replay_lsn(), pg_last_xact_replay_timestamp();

If a standby/PITR is stuck fetching WAL, inspect the archive/restore path:

# is restore_command hanging on an unreachable archive?
grep -i restore_command /var/lib/postgresql/16/main/postgresql*.conf

Check control data to see the last checkpoint and recovery requirements:

pg_controldata -D /var/lib/postgresql/16/main | grep -iE 'state|checkpoint|redo'

Example Root Cause Analysis

A production primary was OOM-killed by the kernel. On restart, every application connection failed with FATAL: the database system is starting up, and it stayed that way for over ten minutes, triggering pager alerts.

Tailing the server log showed the truth — it was not stuck, it was recovering:

LOG:  database system was interrupted; last known up at 2026-07-09 09:12:41 UTC
LOG:  database system was not properly shut down; automatic recovery in progress
LOG:  redo starts at 4A/8B00F3E8
LOG:  recovering ... 000000010000004A0000008C
LOG:  recovering ... 000000010000004A0000008D

The LSNs were advancing, so crash recovery was replaying WAL after the unclean shutdown. Because max_wal_size was large and the last checkpoint was far back, there was a lot of WAL to replay. The correct action was to wait — interrupting it would only restart recovery from the same checkpoint. A few minutes later:

LOG:  redo done at 4A/9F00A110
LOG:  database system is ready to accept connections

The durable fix was tuning checkpoint_timeout/max_wal_size to shorten recovery time and adding memory headroom so the OOM kill wouldn’t recur — not anything about the message itself, which was Postgres behaving correctly.

Prevention Best Practices

  • Shut down cleanly (pg_ctl stop -m fast) — a clean shutdown checkpoints and makes the next start nearly instant, avoiding crash recovery entirely.
  • Tune checkpoint_timeout and max_wal_size to balance write throughput against recovery time; very large values speed steady-state writes but lengthen crash recovery.
  • Prevent the crashes that trigger recovery: size memory to avoid OOM kills, use reliable storage, and protect against power loss.
  • Make health checks and connection pools tolerate this message with retry/backoff during restarts instead of alerting immediately.
  • For standbys and PITR, ensure the WAL archive / restore_command is reachable so startup can’t hang waiting for a segment.
  • Monitor recovery progress (advancing replay LSN) so you can distinguish a normal wait from a genuinely stuck startup.

Quick Command Reference

# Watch recovery progress in the server log
tail -f /var/log/postgresql/postgresql-16-main.log

# Is the server up and in what state?
pg_ctl status -D /var/lib/postgresql/16/main
pg_controldata -D /var/lib/postgresql/16/main | grep -iE 'state|redo'
-- Once connectable: recovery status and progress
SELECT pg_is_in_recovery();
SELECT pg_last_wal_replay_lsn(), pg_last_xact_replay_timestamp();

Conclusion

the database system is starting up is usually Postgres working correctly, not failing — it’s the server telling clients to wait while it boots, replays WAL after a crash, or brings a standby to a consistent state. The one diagnostic that matters is the server log: advancing replay LSNs mean recovery is progressing and you should wait (interrupting only restarts it), while a repeated wait on a single WAL segment means startup is stuck on a missing or unreachable archive. Prevent the long waits by shutting down cleanly, avoiding the crashes that force recovery, and tuning checkpoint settings, and make health checks retry this message rather than page on it.

Free download · 368-page PDF

Fixed it? Get 500 Postgres & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.