PostgreSQL Process Architecture

PostgreSQL is built around a multi‑process model rather than a single threaded one.
When the database starts, a single parent process called postmaster is created.
It is responsible for initializing shared memory, listening for client connections, and spawning child processes.
Each client connection is handled by its own backend process, while a set of auxiliary processes perform background tasks such as checkpointing, WAL writing, and vacuuming.
This design gives strong isolation between connections and a clear separation of concerns, but it also means that every active connection consumes its own memory footprint.

Main Process Components

Postmaster (Parent Process)

The postmaster is the root of the PostgreSQL process tree.
Typical responsibilities include:

  • Starting the database instance and allocating shared memory segments.
  • Listening on the port defined in postgresql.conf (default 5432).
  • Accepting client connections and forking a new backend for each one.
  • Monitoring child processes; if a backend crashes it cleans up and may trigger recovery.
  • Launching the startup process after a crash to replay WAL and bring the system back to a consistent state.

On a running system you can see the postmaster with:

ps -ef | grep postgres | grep -v grep

The first line usually shows the postmaster, followed by the auxiliary processes and any active backends.

Backend Processes

A backend is the workhorse that serves a single client session.
After the postmaster forks it, the backend:

  1. Authenticates the client.
  2. Enters a query loop.
  3. Parses and rewrites SQL statements.
  4. Generates an execution plan and runs it.
  5. Reads from and writes to the shared buffers.
  6. Produces WAL records for durability.
  7. Sends results back over the network socket.

You can inspect the current backend’s PID with:

SELECT pg_backend_pid();

And list all active sessions:

SELECT pid, usename, datname, state, backend_type
FROM pg_stat_activity;

To terminate a runaway backend:

SELECT pg_terminate_backend(12345);  -- replace 12345 with the target PID

Auxiliary Processes

PostgreSQL spawns several helper processes that run independently of client sessions:

Process Role
checkpointer Flushes dirty pages from shared buffers to disk at checkpoints.
background writer Periodically writes dirty pages to reduce I/O spikes.
walwriter Flushes WAL buffers to WAL files, lowering the write load on backends.
autovacuum launcher Starts autovacuum workers to reclaim dead tuples and prevent transaction ID wraparound.
archiver Copies completed WAL segments to an archive directory when archiving is enabled.
syslogger Collects and writes log messages to the configured log destination.
walsender / walreceiver Handles streaming replication by sending or receiving WAL records.
startup Replays WAL during crash recovery.
parallel worker Spawned on demand by backends to execute parallel query plans.

PostgreSQL 15 and later removed the separate stats collector process; statistics are now gathered in shared memory.

Shared Memory & Inter‑Process Communication

All PostgreSQL processes share a set of memory segments and communicate via signals and sockets:

  • shared_buffers – caches data pages; the most critical memory area.
  • WAL buffers – temporary storage for WAL records before they are written to disk.
  • Lock table – tracks table‑ and row‑level locks.
  • Proc array – records information about each active backend.
  • Statistics area – holds runtime metrics.

Communication mechanisms:

  • Shared memory – used for caching, locking, and statistics.
  • Signals – e.g., SIGTERM, SIGQUIT, SIGUSR1 to control process lifecycle and trigger actions.
  • Sockets – client‑backend communication and replication traffic.

Backends never modify data files directly; they modify pages in shared buffers and generate WAL entries. The background writer, checkpointer, and walwriter eventually persist these changes to disk.

Connection Lifecycle Example

A typical client connection proceeds through the following steps:

  1. Client initiates a TCP connection to PostgreSQL’s listening port.
  2. Postmaster accepts the connection and forks a new backend process.
  3. The backend authenticates the user and enters the query loop.
  4. The client sends SQL; the backend parses, plans, and executes it, writing WAL as needed.
  5. Results are sent back over the socket.
  6. When the client closes the connection, the backend exits cleanly.

You can observe this flow with:

ps -ef | grep postgres

Typical output:

postgres  1234     1  0 10:00 ?  00:00:00 /usr/lib/postgresql/16/bin/postgres
postgres  1235  1234  0 10:00 ?  00:00:00 postgres: checkpointer
postgres  1236  1234  0 10:00 ?  00:00:00 postgres: background writer
postgres  1237  1234  0 10:00 ?  00:00:00 postgres: walwriter
postgres  1238  1234  0 10:00 ?  00:00:00 postgres: autovacuum launcher
postgres  1239  1234  0 10:00 ?  00:00:00 postgres: logical replication launcher
postgres  1240  1234  0 10:01 ?  00:00:00 postgres: user db 127.0.0.1(5432) idle

Here, 1234 is the postmaster, 1235‑1239 are auxiliary processes, and 1240 is a backend serving a client.

Process Collaboration During an UPDATE

Consider a simple UPDATE statement:

UPDATE customers SET last_login = now() WHERE id = 42;

The following steps illustrate how multiple processes cooperate:

  1. Backend receives the SQL and parses it.
  2. The planner generates a plan that will scan the customers table, locate the row with id = 42, and modify the last_login column.
  3. The backend acquires a row lock on the target tuple via the lock table in shared memory.
  4. It reads the page containing the tuple from shared buffers (or loads it from disk if not cached).
  5. The backend writes the new tuple value into the buffer and marks the page dirty.
  6. A WAL record describing the change is appended to the WAL buffer.
  7. The walwriter periodically flushes the WAL buffer to a WAL file on disk.
  8. The background writer and checkpointer eventually flush the dirty page to the data file, ensuring durability.
  9. Once the transaction commits, the backend releases the lock and returns a success message to the client.

Throughout this process, the postmaster remains idle, simply monitoring the child processes. The autovacuum launcher may later run a worker to clean up any dead tuples created by the update, but it does not interfere with the current transaction.

Summary

PostgreSQL’s multi‑process architecture offers:

  • Isolation: A crash in one backend rarely affects others.
  • Clear separation: Backends handle client logic, while auxiliary processes manage background maintenance.
  • Scalable concurrency: Each connection gets its own process, which can be tuned with connection pooling for high‑throughput workloads.

The trade‑off is the memory overhead of maintaining a separate process per connection, which is why connection pooling (e.g., PgBouncer, PgPool) is common in production deployments. Understanding how the postmaster, backends, and auxiliary processes interact is essential for diagnosing performance issues, tuning the system, and ensuring reliable operation.