PostgreSQL Cluster and Service Management
Ubuntu wraps PostgreSQL in two layers - a systemd unit per cluster and a `postgresql` umbrella that controls none of them directly. Knowing which to use saves a confusing ten minutes during an incident.
Foundations Guide 5 of 47 Intermediate
- OSUbuntu 26.04 LTS (resolute)
- PostgreSQL18.6-0ubuntu0.26.04.1
- PackagingDebian cluster layout
- TimeAbout 13 min
- Reviewed27 August 2026
Written against the versions above. `pg_ctlcluster` and the per-cluster units are Debian-family only. On RPM distributions the unit is `postgresql-18` and there is no cluster wrapper.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| db-a01 | 192.168.0.81 | Ubuntu 26.04 LTS | Primary / Source / Replica Set Member 1 | 2 Core | 4 GB | 50 GB |
Before you start
- PostgreSQL installed from the Ubuntu packages.
-
Read the unit that actually runs the server
The unit is
postgresql@18-main- templated, one instance per cluster. The plainpostgresqlunit you enabled at install is an umbrella that starts and stops the real ones; it has no process of its own.That matters in an incident.
systemctl status postgresqlreports on the umbrella and can look healthy while a cluster is down. Always name the cluster when something is wrong.bash Example session systemctl status postgresql@18-main --no-pager | head -8● postgresql@18-main.service - PostgreSQL Cluster 18-main Loaded: loaded (/usr/lib/systemd/system/postgresql@.service; enabled-runtime; preset: enabled) Active: active (running) since Thu 2026-08-27 13:49:13 UTC; 34s ago Invocation: 7795f0072de048d5a3387fc08580a093 Process: 25575 ExecStart=/usr/bin/pg_ctlcluster --skip-systemctl-redirect 18-main start (code=exited, status=0/SUCCESS) Main PID: 25580 (postgres) Tasks: 9 (limit: 1658) Memory: 41.9M (peak: 45.2M)Expected resultThe per-cluster unit, loaded and active.
Success conditionYou know which unit corresponds to a running cluster.
-
Use the cluster tool instead
pg_ctlcluster 18 main statusis the Debian-native way, and it takes version and cluster name as arguments rather than needing you to build a unit name.The same family provides
pg_ctlcluster ... start|stop|restart|reload,pg_createclusterandpg_dropcluster. On a machine running two major versions during an upgrade, these are considerably clearer than systemd unit names - andpg_lsclustersfrom the install guide shows them all at once.Both routes work and they are not in conflict:
pg_ctlclustercalls systemd underneath on a systemd host.bash Example session sudo pg_ctlcluster 18 main status ; echo "exit=$?"pg_ctl: server is running (PID: 25580)/usr/lib/postgresql/18/bin/postgres "-D" "/var/lib/postgresql/18/main" "-c" "config_file=/etc/postgresql/18/main/postgresql.conf"exit=0Expected resultThe cluster reported online, with
exit=0.Success conditionYou can manage a cluster by version and name rather than unit string.
Troubleshooting
systemctl status postgresqllooks fine but nothing is listening.Why: That is the umbrella unit. The per-cluster unit is the one that runs a server.
Fix:
pg_lsclustersshows real state. Thensystemctl status postgresql@18-main.A cluster will not start and systemd gives no detail.
Why: PostgreSQL logs to its own file, not the journal, on Debian.
Fix:
/var/log/postgresql/postgresql-18-main.log- the path is inpg_lsclusters.Two clusters, and commands hit the wrong one.
Why: Tools default to the first or to port 5432.
Fix:Pass version and name to
pg_ctlcluster, and-pto psql.pg_lsclustersshows which port belongs to which.