Skip to main content
RunBook Academy

PostgreSQLII · Installation, Packaging and Service ManagementInstallation

Packaging, and where the packaging decides things live

Intermediate⏱ ~25 min🧪 Lab requiredpsql

What you'll learn

  • Compare the Debian, Red Hat and container layouts for the same PostgreSQL version
  • Locate the configuration files and binaries on an unfamiliar host
  • Explain why the Debian cluster wrappers exist and what they add
  • Choose between distribution packages and the PGDG repository from stated criteria

Prerequisites

Practice

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.

Three PostgreSQL installations of the same version can differ so much in layout that a procedure written for one is useless on another. The binaries are identical; where the configuration lives, what starts the service, and whether a second cluster is a supported operation are all decisions the packaging made.

This matters because runbooks are written once and used on whichever host is having the incident. A step that says “edit /etc/postgresql/18/main/postgresql.conf” is wrong on Red Hat and wrong in a container, and at 03:00 that costs more than the five minutes it would have taken to write the step properly.

The three layouts

Debian / Ubuntu (PGDG)Red Hat / Rocky (PGDG)Official container image
Binaries/usr/lib/postgresql/18/bin//usr/pgsql-18/bin//usr/lib/postgresql/18/bin/
Data/var/lib/postgresql/18/main//var/lib/pgsql/18/data//var/lib/postgresql/18/docker/
Configuration/etc/postgresql/18/main/inside the data directoryinside the data directory
Servicepostgresql@18-main.servicepostgresql-18.servicePID 1, no init system
Multi-clusterfirst-class, via wrappersmanualone per container
Cluster toolingpg_ctlcluster, pg_lsclustersnonepresent but unused

The single most consequential difference is the configuration location. Debian moves postgresql.conf, pg_hba.conf and pg_ident.conf out of the data directory into /etc, which is where a system administrator expects configuration to be and which makes the files easy to manage with configuration management. Red Hat leaves them in PGDATA, which is where PostgreSQL itself puts them and which keeps a cluster self-contained.

Neither is wrong. Both are load-bearing assumptions in a procedure.

The Debian cluster model

Debian’s packaging treats “several clusters on one host” as normal rather than exceptional, and supplies tooling for it. A cluster has a version and a name, and the pair identifies it everywhere.

Read-only / Safecreating a genuine second cluster on a host that already has one
$ pg_createcluster 18 reporting
selecting default "shared_buffers" ... 128MB
selecting default time zone ... Etc/UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
Ver Cluster   Port Status Owner    Data directory                   Log file
18  reporting 5433 down   postgres /var/lib/postgresql/18/reporting /var/log/postgresql/postgresql-18-reporting.log

Note what it did without being asked: allocated port 5433 because 5432 was taken, created a data directory named after the cluster, and set up a log file. That is the value the wrappers add — the bookkeeping that otherwise has to be done by hand and remembered.

Service impact possiblestarting the second cluster and confirming it is a separate server
$ pg_ctlcluster 18 reporting start && pg_lsclusters
Ver Cluster   Port Status Owner    Data directory                   Log file
18  reporting 5433 online postgres /var/lib/postgresql/18/reporting /var/log/postgresql/postgresql-18-reporting.log

-- and each answers for itself
$ psql -p 5433 -tAc 'SHOW data_directory'
/var/lib/postgresql/18/reporting

$ psql -p 5432 -tAc 'SHOW data_directory'
/var/lib/postgresql/18/docker

Two servers, sharing nothing but the kernel and the disk. This is the mechanism behind the isolation discussion in the first lesson of this course: separate clusters are how you get independent restart windows and independent restore points, and on Debian creating one is a single command.

The wrappers to know:

CommandDoes
pg_lsclustersList every registered cluster with version, port, status, data directory
pg_createcluster 18 nameCreate a new cluster, allocating a free port
pg_ctlcluster 18 name start|stop|restart|reloadControl one cluster
pg_dropcluster 18 nameRemove a cluster and its data
pg_upgradecluster 17 mainMajor-version upgrade of one cluster (Part XVII)

The container image is neither layout

The official postgres image is built from the Debian packages, so the binaries live in /usr/lib/postgresql/18/bin/ and the Debian cluster wrappers are present on the filesystem. But the image does not register its cluster with them.

Read-only / Safepg_lsclusters inside the official image, with PostgreSQL running
$ docker exec rbpg-multi pg_lsclusters
Ver Cluster Port Status Owner Data directory Log file

PostgreSQL is running, serving on 5432, and pg_lsclusters reports nothing. The reason is that the wrappers enumerate clusters by reading /etc/postgresql/<version>/<name>/, and the image keeps its configuration inside PGDATA in the Red Hat style instead. There is no registration to find.

This is worth knowing for a practical reason: a monitoring check or an Ansible role that uses pg_lsclusters to discover clusters silently finds nothing in a container, and “no clusters” is indistinguishable from “no PostgreSQL” in most such scripts.

The image also runs PostgreSQL as PID 1 with no init system, which changes the shutdown story. Stopping the container sends a signal to PostgreSQL directly, and which signal it is decides whether you get a clean shutdown or a crash recovery on next start. Part XII covers the shutdown modes and Part XVIII covers the container-specific consequences.

Distribution packages or PGDG

Most distributions ship PostgreSQL in their own repositories, and the PostgreSQL project maintains its own APT and YUM repositories carrying every supported major version.

Distribution packages give you the version the distribution chose, supported for the distribution’s lifetime, integrated with its security updates. For Debian and Ubuntu that is usually one major version per release, and it may be several years old by the end of the release’s life.

PGDG packages give you every supported major version, packaged consistently, available on the day upstream releases. Several majors can be installed side by side, which is what makes pg_upgrade practical.

The criteria that actually decide it:

  • Do you need a major version the distribution does not ship? If yes, PGDG, and the question is settled.
  • Do you need two majors installed at once? pg_upgrade requires the binaries of both the old and the new version present simultaneously. Distribution repositories rarely allow this; PGDG is designed for it. This is the reason most production estates end up on PGDG regardless of where they started.
  • Does your patching process consume one repository or several? Adding PGDG means one more source to track, sign-verify and pin.
  • Is the host managed by a policy that forbids third-party repositories? Then the choice is made for you, and the version ceiling is a constraint to plan the upgrade calendar around.

Identifying an unfamiliar host

# Which layout am I on? The config file location answers it.
psql -U postgres -tAc 'SHOW config_file'
psql -U postgres -tAc 'SHOW hba_file'
psql -U postgres -tAc 'SHOW data_directory'

# Which PostgreSQL versions have binaries installed?
ls -d /usr/lib/postgresql/*/bin /usr/pgsql-*/bin 2>/dev/null || true

# Debian: are there registered clusters?
command -v pg_lsclusters >/dev/null 2>&1 && pg_lsclusters || echo "no debian cluster tooling"

# Which units exist, and which are running?
systemctl list-units --type=service 'postgres*' --all 2>/dev/null || true

A config file under /etc/postgresql/ means Debian packaging. A config file inside the data directory means Red Hat packaging or a container. The presence of several directories under /usr/lib/postgresql/ means several majors are installed, which is either an upgrade in progress or an upgrade that was never finished.

Production discipline

  1. Read paths from the server, not from memory. SHOW config_file, SHOW hba_file and SHOW data_directory are correct on every layout.
  2. Write runbooks with the discovery step included. A literal path is a portability bug that surfaces during an incident on the one host that differs.
  3. Choose PGDG when you need side-by-side majors. pg_upgrade needs both versions’ binaries present, and that requirement decides most estates.
  4. Pin the repository origin for PostgreSQL packages. Mixing distribution and PGDG builds produces extension mismatches that appear at use time, not install time.
  5. Treat pg_dropcluster as a destructive command. It removes the data directory, and the only thing distinguishing the right cluster from the wrong one is a word you type.
  6. Remember that on Debian, PGDATA is not the whole cluster. The configuration lives in /etc and a naive archive of PGDATA contains no configuration at all.

Cross-course references

  • Linux for Production Sysadmins — Part XI (Packages) and Part XII (Repository security) cover repository pinning and signature verification, which is the mechanism behind the mixing warning above.
  • Ansible for Production Sysadmins — Part XXXIX (Platforms) covers writing roles that work across distributions, which is the automated form of the discovery step in this lesson.
  • Docker & Containers — Part IV (Images) covers what the official image is built from, and Part X (Production architecture) covers running a process as PID 1.

Quiz

Knowledge check · 6 questions

  1. Q1. A monitoring check discovers PostgreSQL clusters by running pg_lsclusters. It reports zero clusters on a host where PostgreSQL is demonstrably running in the official container image. Why?

  2. Q2. Which single requirement most often drives a production estate onto the PGDG repositories rather than distribution packages?

  3. Q3. Which commands or queries give a layout-independent answer on an unfamiliar PostgreSQL host? Select all that apply.

  4. Q4. On a Debian host, a physical copy of the PGDATA directory captures the complete cluster including its configuration files.

  5. Q5. pg_dropcluster removes a cluster's registration and also deletes its data directory.

  6. Q6. Identify why the procedure failed and rewrite the affected steps.

    A runbook for enabling statement duration logging instructs the responder to edit /etc/postgresql/18/main/postgresql.conf and then run systemctl reload postgresql@18-main. It has worked for two years. During an incident on a newly migrated host it fails: the file does not exist and the systemctl unit is not found. The host is running PostgreSQL 18.6 and the database is serving traffic normally. The migration moved this workload from Ubuntu virtual machines to Rocky Linux hosts installed from the PGDG YUM repository.

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