Skip to main content
RunBook Academy

PostgreSQLI · Architecture and the Process ModelArchitecture

What a PostgreSQL "database cluster" actually is

Foundation⏱ ~25 minpsql

What you'll learn

  • State what a PostgreSQL database cluster contains and what it does not
  • Distinguish the PostgreSQL meaning of cluster from the high-availability meaning used elsewhere
  • Predict which objects are shared across every database and which are private to one
  • Explain why a single database cannot be restarted, replicated or relocated on its own

Prerequisites

None — start here.

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

Not yet marked complete on this device.

PostgreSQL uses the word cluster to mean something that almost nobody else in infrastructure means by it. In Kubernetes, in Ceph, in Pacemaker and in ordinary conversation, a cluster is several machines cooperating. In PostgreSQL, a cluster is one running server on one machine, managing one data directory, serving a collection of databases. There is no second machine involved and no cooperation to speak of.

This is not a pedantic distinction to get out of the way in the first five minutes. It decides what you can back up separately, what you can restart separately, what replication actually copies, and what “migrate that database to its own server” costs. Engineers who carry the everyday meaning of the word into PostgreSQL make a specific class of planning error, and they make it repeatedly, because the wrong mental model keeps producing plausible answers.

The unit that actually exists

A PostgreSQL cluster is the set of things that share a data directory. Concretely, one cluster is:

  • one PGDATA directory on disk;
  • one listening port;
  • one postmaster process and its family of background processes;
  • one write-ahead log;
  • one set of shared system catalogues;
  • one configuration;
  • and the databases created inside it.

Everything in that list is singular. The databases are the only plural item, and they are tenants rather than peers: they share every resource above them and are isolated only in the narrow sense that their tables cannot see each other.

Ask the running server where its boundary is and it answers in two values.

Read-only / Safethe two values that identify a cluster
$ psql -U postgres -c 'SHOW data_directory' -c 'SHOW port'
        data_directory
-------------------------------
/var/lib/postgresql/18/docker
(1 row)

port
------
5432
(1 row)

Two clusters on one host are two data directories and two ports, two postmasters, two write-ahead logs and two independent failure domains. They share the kernel, the page cache and the disk, and nothing else. That is a real deployment pattern and it is covered later in this part of the course; the point here is that it is the only way to get independence between databases, because inside a single cluster there is none.

What every database shares

A fresh cluster has three databases before you create anything.

Read-only / Safethe three databases initdb creates
$ psql -U postgres -c 'SELECT datname, datallowconn, datconnlimit FROM pg_database ORDER BY datname'
  datname  | datallowconn | datconnlimit
-----------+--------------+--------------
postgres  | t            |           -1
template0 | f            |           -1
template1 | t            |           -1
(3 rows)

template1 is the pattern: CREATE DATABASE copies it, so an extension or a table installed into template1 appears in every database created afterwards and in none created before. template0 is the pristine copy kept in reserve, and datallowconn is f precisely so that nobody can modify it. postgres is a conventional default database for tools and administrators to connect to; it holds nothing special.

Now the part that matters operationally. Some objects live above the database level and are visible from all of them. Roles are the clearest example. Create one while connected to any database, then look for it from a different database.

psql -U postgres -c "CREATE ROLE reporting_ro LOGIN PASSWORD 'lab-password-not-a-real-secret'"
createdb -U postgres shop
psql -U postgres -d shop -c "SELECT rolname FROM pg_roles WHERE rolname = 'reporting_ro'"
Read-only / Safea role created in one database is visible from another
$ psql -U postgres -d shop -c "SELECT rolname FROM pg_roles WHERE rolname = 'reporting_ro'"
   rolname
--------------
reporting_ro
(1 row)

Tables behave in the opposite way. A table created in shop is not merely inaccessible from postgres — it is not in that database’s catalogue at all.

Read-only / Safethe same catalogue query, two different databases, two different answers
$ psql -U postgres -d postgres -c "SELECT count(*) AS orders_visible FROM pg_class WHERE relname = 'orders'"
-- run against the shop database
count
-------
   1
(1 row)

-- the identical query against the postgres database
orders_visible
----------------
            0
(1 row)

The dividing line is worth memorising, because it predicts the answer to a great many operational questions.

Cluster-wide (shared by every database)Per-database (private)
Roles and their passwords and membershipsSchemas
TablespacesTables, indexes, views, sequences
The write-ahead logMost system catalogues, including pg_class
Configuration (postgresql.conf, pg_hba.conf)Extensions, installed per database
Background processes and shared memoryDefault privileges
Replication and physical backupsDatabase-scoped settings
The transaction ID counterObject ownership records

The consequences you will actually meet

You cannot query across databases. This is not a permissions problem and it is not a syntax you have not learned yet. PostgreSQL declines by name.

Read-only / Safea three-part name naming another database
$ psql -U postgres -d postgres -c 'SELECT * FROM shop.public.orders'
ERROR:  cross-database references are not implemented: "shop.public.orders"
LINE 1: SELECT * FROM shop.public.orders;
                    ^

Reaching another database means a foreign data wrapper or dblink — that is, a client connection opened from inside the server, with all the authentication, latency and failure behaviour a client connection has. Teams that split one application’s data across two databases in the same cluster “for isolation” usually discover this the week they need a join.

You cannot restart one database. There is no per-database service. Restarting to pick up a postgresql.conf change interrupts every database in the cluster at once, so a change requested by one application team is a change window for all of them. This is why the distinction between a reload and a restart, covered later in this part, is worth real attention.

Physical replication is cluster-wide. A streaming replica is a copy of the whole data directory kept current by replaying the whole write-ahead log. You cannot stream one database to a standby and leave the rest behind. When somebody asks for “a read replica of just the reporting database”, the honest answer is either a replica of everything or logical replication, which is a different mechanism with different limits.

Physical backups are cluster-wide too. A base backup plus archived WAL restores a cluster to a point in time, all databases together. If one team needs a restore to 14:03 and another must not lose the work it did at 14:30, a single cluster cannot satisfy both, and the restore lands in a separate instance while somebody extracts what is needed. Logical backups with pg_dump are per-database and per-object, which is exactly why both kinds of backup exist and why later parts of this course refuse to treat either as the default answer.

Resource exhaustion is cluster-wide. max_connections counts connections to the whole cluster, not per database. One application opening six hundred connections denies service to every other database in the same instance. The same is true of disk: they share a filesystem.

Terminology discipline for the rest of this course

Because the word is genuinely overloaded, this course is deliberate about it.

  • Database cluster, or just cluster — the PostgreSQL meaning: one instance, one data directory, one port, many databases.
  • Instance — used as a synonym for cluster where it reads more naturally. PostgreSQL’s own documentation treats them as interchangeable.
  • High-availability cluster, or HA cluster — several machines, one of them primary, arranged so that the service survives losing one. Always written out in full. It is the subject of Part XV and it is not what initdb creates.
  • Database — one tenant inside a cluster.

When a monitoring dashboard, a vendor document or a colleague says “cluster”, establish which one they mean before you act on it. The sentence “the cluster is down” means an outage in one vocabulary and a single crashed process in the other.

Reading a cluster you have just inherited

Four questions, four commands, in the order that keeps you from guessing. Run them against any PostgreSQL server you are handed.

# 1. Which server is this, and which major version?
psql -U postgres -tAc 'SELECT version()'

# 2. Where does it keep its data, and what port does it answer on?
psql -U postgres -c 'SHOW data_directory' -c 'SHOW port'

# 3. What is inside it, and how big is each tenant?
psql -U postgres -c \
  "SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size,
          datallowconn, datconnlimit
     FROM pg_database ORDER BY pg_database_size(datname) DESC"

# 4. Is anything else running on this host?
ls -d /var/lib/postgresql/*/ 2>/dev/null || true

The fourth is the one people skip. A host with two data directories has two clusters on two ports, and connecting to 5432 out of habit while investigating an incident in the cluster on 5433 wastes the first twenty minutes of an outage. The evidence for which cluster you are attached to is data_directory, not the hostname.

Production discipline

  1. Establish which meaning of “cluster” is in play before acting. In an incident this takes one sentence and prevents an entire class of wrong action.
  2. Treat a cluster as one blast radius. Restart, restore, upgrade, connection budget and disk are all shared. Databases inside it are tenants, not isolated services.
  3. Record the data directory and port in every runbook and ticket. They identify the cluster unambiguously; the hostname does not, and the database name certainly does not.
  4. Do not split one application’s data across databases for isolation. You lose cross-database queries and transactional consistency, and you gain almost nothing, because everything that actually constrains you is still shared.
  5. Decide backup granularity from this boundary. If a restore requirement is per-database and per-point-in-time simultaneously, one cluster cannot serve it and the architecture needs to change before the incident, not during it.

Cross-course references

  • Linux for Production Sysadmins — Part XV (Fstab) and Part XVIII (Storage) cover the filesystem beneath PGDATA, which is the physical boundary this lesson is describing.
  • Docker & Containers — Part VIII (Storage) covers volume lifetime, which decides whether a containerised cluster’s data directory survives the container.
  • Kubernetes for Production Sysadmins — Part LIV (Stateful Workloads) covers the identity and storage guarantees a StatefulSet gives, which is the mechanism a cluster-per-pod deployment relies on.

Quiz

Knowledge check · 5 questions

  1. Q1. An application team asks for a streaming replica of just the reporting database, which is one of five databases in a production cluster. What is the accurate response?

  2. Q2. Which of these are shared across every database in one PostgreSQL cluster? Select all that apply.

  3. Q3. Restarting PostgreSQL to apply a configuration change interrupts every database in the cluster, because a single postmaster serves all of them.

  4. Q4. You are handed an unfamiliar PostgreSQL host during an incident. Name the two values that unambiguously identify which cluster you are connected to, and say why the hostname is not sufficient.

  5. Q5. Assess the proposal and state what it does and does not achieve.

    A platform team runs one PostgreSQL cluster hosting eight application databases. After an incident in which one application opened 400 connections and the other seven applications began failing to connect, the team proposes to prevent recurrence by giving each application its own database within the same cluster, which is already the case, and by setting a per-database connection limit with ALTER DATABASE ... CONNECTION LIMIT. They also propose that backups be taken per database so each team can restore independently.

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