DB Internals AtlasPostgreSQL 18.6 Home About

Architecture

Server startup sequence

Server startup is a postmaster state machine. PostgreSQL initializes the cluster environment, launches the startup process, performs WAL recovery when required, starts the appropriate background processes, and only then reaches a state that can serve normal connections.

In plain words

Starting PostgreSQL means starting one main process, the postmaster. Tools like pg_ctl start or systemd just launch it with the right data folder.

The postmaster reads the configuration, sets up the shared memory every session will use, writes a postmaster.pid file and starts listening for connections. Then it starts a helper, the startup process, whose job is to check whether the last shutdown was clean.

After a clean shutdown there is nothing to repair, so the server is ready almost at once. After a crash, the startup process first replays the write-ahead log (WAL) to bring the data files back to a consistent state, which can take a while. A standby server never really finishes this step: it keeps replaying WAL from its primary, and can accept read-only queries meanwhile.

Only when this is done does the server accept normal read-write connections. The animation lets you compare the three paths.

For DBAs and operators

The sequence

  1. Launch: postgres -D ..., pg_ctl start, or the operating-system service starts the supervisor process. pg_ctl -w waits until the PID-file status reports that the server is ready.
  2. PM_INIT: the postmaster processes configuration and cluster paths, checks the data directory/control information, establishes the lock/PID file, initializes shared memory/semaphores, and opens the configured listen sockets.
  3. PM_STARTUP: it launches the startup process. PostgreSQL also starts infrastructure needed during startup/recovery, including the checkpointer/background writer and, when configured, PostgreSQL 18 I/O workers.
  4. StartupXLOG(): the startup process examines pg_control and WAL/recovery configuration. A clean primary startup can finish quickly; an unclean state requires redo; archive/standby recovery can keep the startup process alive for much longer.
  5. Recovery states: as soon as WAL redo begins (crash recovery as well as archive/standby recovery) the startup process signals the postmaster, which moves to PM_RECOVERY; a clean start without redo goes straight from PM_STARTUP to PM_RUN. Once a consistent recovery point is reached and hot standby is enabled, the postmaster can enter PM_HOT_STANDBY and accept read-only connections while replay continues.
  6. PM_RUN: after the startup process exits successfully at the end of startup/recovery, the postmaster enters normal running state and starts/maintains the remaining server processes allowed in that state.

Clean start versus crash recovery

A clean shutdown leaves control/WAL state that tells the next startup that normal shutdown completed. An immediate shutdown, power loss, operating-system crash, or unexpected postmaster/backend crash can leave the cluster requiring WAL redo. Recovery starts from the checkpoint REDO location and replays WAL needed to make the data files consistent; it is not simply "replaying from the checkpoint record".

Standby startup is deliberately different

During standby/archive recovery, the startup process may remain alive indefinitely. PostgreSQL can accept read-only sessions in PM_HOT_STANDBY while WAL continues to be restored/streamed and replayed. Promotion or completion of recovery eventually moves the server into PM_RUN.

What operators should watch

  • postmaster.pid exists while the server is running and carries a status used by pg_ctl -w; do not manually remove it while a server may still be alive.
  • Crash recovery can legitimately make startup take far longer than a clean start. Service-manager startup timeouts should account for this.
  • log_startup_progress_interval can report long startup/recovery operations such as data-directory synchronization or WAL redo.

PostgreSQL 18 source trail: postmaster.c defines the PM_INIT → PM_STARTUP → PM_RECOVERY/PM_HOT_STANDBY → PM_RUN state machine. startup.c shows StartupProcessMain() calling StartupXLOG(). See also the PostgreSQL 18 server-start documentation.

For developers: inside the source

PostmasterMain(), in order

SelectConfigFiles()          read postgresql.conf / auto.conf
checkDataDir()               permissions, PG_VERSION
CreateDataDirLockFile()      postmaster.pid
LocalProcessControlFile()    read pg_control
process_shared_preload_libraries()   _PG_init() of extensions
InitializeShmemGUCs(), CreateSharedMemoryAndSemaphores()
ListenServerPort() …         TCP and Unix sockets
RemovePgTempFiles()
UpdatePMState(PM_STARTUP); maybe_adjust_io_workers()
StartChildProcess(B_CHECKPOINTER), (B_BG_WRITER), (B_STARTUP)
ServerLoop()

The helpers live in miscinit.c (lock file, process_shared_preload_libraries()), guc.c and ipci.c. The status line written into postmaster.pid (starting, ready, standby, stopping) and its line numbers are defined in pidfile.h; pg_ctl -w polls it in wait_for_postmaster_start() (pg_ctl.c).

The startup process

StartupProcessMain() calls StartupXLOG(). It reads ControlFileData (pg_control.h), then InitWalRecovery() looks for standby.signal/recovery.signal, finds the checkpoint record and decides whether redo is needed. If it is, PerformWalRecovery() runs the redo loop. At the end, PerformRecoveryXLogAction() writes an end-of-recovery record on promotion, otherwise it asks the checkpointer for an end-of-recovery checkpoint and waits for it.

How the postmaster learns about progress

The startup process uses SendPostmasterSignal() (pmsignal.c); the postmaster handles it in process_pm_pmsignal():

  • PMSIGNAL_RECOVERY_STARTED: sent by PerformWalRecovery() whenever redo begins, for crash recovery too; PM_STARTUP → PM_RECOVERY.
  • PMSIGNAL_BEGIN_HOT_STANDBY: sent from CheckRecoveryConsistency() once consistent with hot standby possible; → PM_HOT_STANDBY, "database system is ready to accept read-only connections".
  • Startup process exit code 0 → PM_RUN in process_pm_child_exit(); LaunchMissingBackgroundProcesses() then starts the WAL writer, autovacuum launcher, archiver and bgworkers allowed in that state.

Experiment and extend

  • log_min_messages = debug2 shows the postmaster's internal messages, such as "postmaster received pmsignal signal".
  • TAP: src/test/postmaster/t/003_start_stop.pl, and most of src/test/recovery/t/ (crash restart, PITR, standby).
  • Extensions loaded through shared_preload_libraries run _PG_init() in the postmaster before shared memory exists: request space in shmem_request_hook, initialise it in shmem_startup_hook, and register background workers there.

Parameters that shape this

data_directory shared_buffers listen_addresses port unix_socket_directories hot_standby restart_after_crash io_method io_workers log_startup_progress_interval

Related topics

Server shutdown modes Postmaster Startup process Crash recovery Checkpointer I/O workers Process model Write-ahead log (WAL)