
To put PostgreSQL 18 into service on Debian, you add the PGDG repository (PostgreSQL Global Development Group, the project's team), bind it to its signing key after checking the fingerprint, then install postgresql-18. This lesson is for anyone administering a first database server. It takes you from a blank machine to an instance that is enabled at boot, answers pg_isready, has checksums enabled and does not listen on the network yet. Opening access, tuning the server and hardening it each have their own lesson.
What you will learn
- Declare the PGDG repository in
deb822format, bound to its key bySigned-By - Verify the key's fingerprint before trusting it
- Install
postgresql-18and recognise the18/maincluster that the package creates - Check boot-time startup,
pg_isready, checksums and network listening - Tell apart the Debian installation from the Rocky Linux and RHEL one
Prerequisites
- A blank Debian 13 server (or Debian 12) with no PostgreSQL, 2 vCPU and 2 GB of RAM are enough, with
sudoaccess. - The basics of APT, explained in Manage packages with APT, including the
Signed-Byfield. - The cluster, instance and PGDATA vocabulary from Découvrir PostgreSQL (in French).
- An Internet connection to
apt.postgresql.organdwww.postgresql.org.
Why not install Debian's postgresql package?
Debian ships its own PostgreSQL, frozen for the whole life of the distribution: on Debian 13 that is version 17, not 18. A plain sudo apt install postgresql on a fresh machine therefore gives you a working instance, but of a different major version from the one you are aiming for, and you only find out by reading pg_lsclusters.
The project publishes an official package repository for this, one per Debian release, named <codename>-pgdg (trixie-pgdg for Debian 13, bookworm-pgdg for Debian 12). It holds every supported major version, installable side by side. Before installing anything, apt-cache policy postgresql shows what your repositories offer: as long as apt.postgresql.org is not listed there, version 18 is not available.
How do you declare the PGDG repository and its key?
You have to tell APT where the repository is and which key signs it. Without that binding, APT refuses the packages (NO_PUBKEY), and working around it with trusted=yes switches off all signature checking: you would accept any package coming from that server. The key goes in a dedicated file under /etc/apt/keyrings/, never in the global keyring.
-
Download the signing key
The project publishes the repository key under the identifier
ACCC4CF8. The/etc/apt/keyrings/directory is the place intended for the keys of a third-party repository, andcurl -fsSLmakes the download fail on an HTTP error:Terminal window sudo apt-get install -y curl ca-certificates gnupgsudo install -d -m 0755 /etc/apt/keyringssudo curl -fsSL -o /etc/apt/keyrings/pgdg.asc \https://www.postgresql.org/media/keys/ACCC4CF8.asc -
Verify the fingerprint before trusting the key
Downloading a key does not prove it is the right one. Its fingerprint (a unique digest) is compared with the one the project announces, using
gpg, which reads the key without importing it:Terminal window gpg --show-keys --with-fingerprint /etc/apt/keyrings/pgdg.ascThe fingerprint of the PostgreSQL repository key (identity
PostgreSQL Debian Repository, RSA 4096) isB97B 0AFC AA1A 47F0 44F2 44A0 7FCC 7D46 ACCC 4CF8. If it differs, delete the file and do not go on. -
Declare the repository, bound to the key
The
deb822format stores the repository in a.sourcesfile. TheSigned-Byfield points at the key above;Suitesholds your Debian codename followed by-pgdg:Terminal window sudo tee /etc/apt/sources.list.d/pgdg.sources > /dev/null <<'EOF'Types: debURIs: https://apt.postgresql.org/pub/repos/aptSuites: trixie-pgdgComponents: mainSigned-By: /etc/apt/keyrings/pgdg.ascEOFOn Debian 12, replace
trixie-pgdgwithbookworm-pgdg. Your machine's codename is in/etc/os-release(VERSION_CODENAME). -
Refresh the index and check
Terminal window sudo apt-get updateThe output must mention
apt.postgresql.orgwithoutNO_PUBKEY,not signedor any signature warning. That is exactly what the lab checks: the key is judged by its effect, not by its file name.
How do you install postgresql-18 and recognise its cluster?
The installation takes one command, but what it creates is worth reading. On Debian and Ubuntu, the postgresql-18 package creates and starts a cluster automatically, named main, thanks to the multi-cluster manager postgresql-common. In PostgreSQL terms, a cluster is a data directory (PGDATA) served by one instance, not a set of machines.
sudo apt-get install -y postgresql-18The installation pulls in its dependencies: postgresql-client-18, postgresql-18-jit, postgresql-common and postgresql-client-common. Now check what you got:
pg_lsclustersVer Cluster Port Status Owner Data directory Log file18 main 5432 online postgres /var/lib/postgresql/18/main /var/log/postgresql/postgresql-18-main.logThe Ver column must read 18 and Status must read online. A 17 line means you installed Debian's own package: add the PGDG repository first, then install postgresql-18. The packages themselves are read with dpkg:
dpkg -l 'postgresql*' | grep '^ii'ii postgresql-18 18.6-1.pgdg13+2 amd64 The World's Most Advanced Open Source Relational Databaseii postgresql-18-jit 18.6-1.pgdg13+2 amd64 LLVM JIT support for PostgreSQL 18ii postgresql-client-18 18.6-1.pgdg13+2 amd64 front-end programs for PostgreSQL 18ii postgresql-client-common 293.pgdg13+1 all manager for multiple PostgreSQL client versionsii postgresql-common 293.pgdg13+1 all PostgreSQL database-cluster managerThe pgdg13 suffix proves the package comes from PGDG and not from a Debian repository. The lab applies this same check: the version must start with 18. and contain pgdg.
| Package | Contents |
|---|---|
postgresql-18 | The PostgreSQL server, initdb, the background workers |
postgresql-client-18 | The client tools: psql, pg_dump, pg_restore, pg_isready |
postgresql-common | Debian's multi-cluster manager: pg_lsclusters, pg_ctlcluster, systemd scripts |
postgresql-18-jit | JIT compilation (LLVM) of complex queries |
The data lives in /var/lib/postgresql/18/main (PGDATA) and the configuration in /etc/postgresql/18/main/, two separate locations produced by Debian packaging. This split, postgresql.conf and pg_hba.conf included, is explained in Configurer PostgreSQL (in French), and opening access in Administer a PostgreSQL instance with psql.
Does the instance come back after a reboot?
An active instance is not an enabled instance: the server may answer now and be gone at the next boot. On Debian, two conditions must both hold for it to come back: the umbrella unit postgresql.service is enabled, and the cluster is set to automatic start, which the value auto in the start.conf file declares.
postgresql.service ← umbrella unit (enabled at boot) └── postgresql@18-main ← instance of the 18/main clusterThe packaging uses a systemd template (postgresql@.service): one unit per cluster, which lets you run several versions on the same machine. The three useful reads take three commands:
systemctl is-enabled postgresqlsystemctl is-active postgresql@18-maingrep -v '^#' /etc/postgresql/18/main/start.conf | grep -v '^$'The first must answer enabled, the second active, the third auto. The grep -v '^#' drops the six comment lines that start.conf carries at the top: the effective value comes after. These three answers are the ones the packaging provides for: they could not be captured in the test container, which has no systemd (see the "Version used" box). The lab goes further than these reads: it reboots the machine and checks that the instance came back, the only way to tell an active service from an enabled one.
To check afterwards that the server accepts connections, without opening an SQL session, pg_isready is enough:
pg_isready/var/run/postgresql:5432 - accepting connectionsThe lab calls it twice: through the Unix socket (output above) and over local TCP with pg_isready -h 127.0.0.1, whose output then names the address and the port:
127.0.0.1:5432 - accepting connectionsA no response means a stopped or unreachable server, and rejecting connections a server that is starting up, shutting down or recovering: in both cases, read the log described in the Troubleshooting section. The first real connection, with psql, is the subject of Administer a PostgreSQL instance with psql.
Are checksums enabled?
Since PostgreSQL 18, data checksums are enabled by default when a cluster is initialised. They detect silent corruption of an 8 KB page on disk (bit flip, failing controller, buggy firmware): without them, PostgreSQL reads corrupted data without knowing it. On earlier versions, you had to ask initdb for them or enable them offline with pg_checksums.
The setting is checked in two places, and the lab requires both: what the server announces, and what the disk holds. A cluster created by this package has checksums enabled. For a cluster coming from elsewhere (initialised by hand, restored, migrated), presume nothing: in testing, a version 17 cluster without checksums migrated with pg_upgradecluster produced a version 18 cluster with checksums, so the old cluster's setting is not carried over by this method, but nothing guarantees the same result with another one. Hence the point of checking rather than assuming.
sudo -u postgres psql -X -At -c 'SHOW data_checksums'onThe cluster's control file, read by pg_controldata, gives the version of the checksum format written to disk:
sudo -u postgres /usr/lib/postgresql/18/bin/pg_controldata /var/lib/postgresql/18/main | grep -i checksumData page checksum version: 1A value of 0 would mean no checksum is written, even if the server suggests otherwise. The cost is a slight CRC computation each time a page is written, negligible on modern hardware: do not disable them.
Does a fresh server listen only on the loopback?
A freshly installed instance does not listen on the network: listen_addresses is localhost, so only connections coming from the machine itself (127.0.0.1 and ::1) succeed. This is a safe default: you open access later, deliberately, once you know to whom. The check combines two reads:
ss -H -ltn | grep ':5432'sudo -u postgres psql -X -At -c 'SHOW listen_addresses'LISTEN 0 200 127.0.0.1:5432 0.0.0.0:*LISTEN 0 200 [::1]:5432 [::]:*localhostThe first lists the real listeners on port 5432: their addresses must be 127.0.0.1 and [::1], never 0.0.0.0 or *. The second must answer localhost, the default value. The lab's test has a deliberate trap: "the server listens only on the loopback" is true on a machine where PostgreSQL is not installed, since no listener exists. So it first requires that a listener exists on 5432, then that all of them are local.
Opening network access (listen_addresses and a host line in pg_hba.conf) is taught in Administer a PostgreSQL instance with psql. That parameter requires a restart, which Configurer PostgreSQL (in French) details with pending_restart. Full hardening is covered in Sécuriser PostgreSQL (in French).
What about Rocky Linux, RHEL or AlmaLinux?
On the Red Hat family, the principle is the same (a PGDG repository, a versioned package), but the installation does not create the cluster: you have to initialise it yourself, then enable the service. These commands are not covered by the lab, which targets Debian 13: they come from the project's documentation and were not replayed for this lesson.
-
Install the PGDG repository
The example targets EL-9 / x86_64. For another version or architecture, generate the command on the PGDG Red Hat page:
Terminal window sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm -
Disable the distribution's PostgreSQL module
RHEL 9 and Rocky Linux 9 ship an AppStream module that conflicts:
Terminal window sudo dnf -qy module disable postgresql -
Install PostgreSQL 18
Terminal window sudo dnf install -y postgresql18-server postgresql18 -
Initialise the cluster, then start and enable the service
Terminal window sudo /usr/pgsql-18/bin/postgresql-18-setup initdbsudo systemctl enable --now postgresql-18
The data directory is then /var/lib/pgsql/18/data/, configuration included, and the service is called postgresql-18. This is the difference behind "my PostgreSQL won't start": without initdb, there is nothing to start.
Put it into practice
The lab hands you db1.lab, a blank Debian 13 machine: no PostgreSQL, no repository for it. You declare the PGDG repository bound to its key, verify the fingerprint, install postgresql-18 and leave the 18/main cluster in service. The validation reads what the machine really runs: the repository as APT knows it (without trusted=yes, with a Signed-By that exists), an apt-get update with no signature complaint, a version 18 package coming from PGDG, an online cluster enabled at boot, checksums on the server side and on disk, and listeners on 5432 that are all local. A final test reboots the machine and checks that the instance comes back.
Troubleshooting
Almost every failure comes from the origin of the package, from the key, or from a service started without being enabled. Read pg_lsclusters (the cluster's version) and apt-cache policy postgresql-18 (the package's origin) first: they settle the first symptom in the table, and the output of apt-get update the second.
| Symptom | Likely cause | Fix |
|---|---|---|
pg_lsclusters shows version 17 | You installed Debian's package | Declare the PGDG repository, then install postgresql-18 |
NO_PUBKEY or not signed at apt-get update | Key missing, or wrong Signed-By path | Download the key again into /etc/apt/keyrings/ and fix Signed-By |
| The instance answers but vanishes at boot | Active but not enabled | systemctl is-enabled postgresql, then sudo systemctl enable postgresql |
A listener on 0.0.0.0 or * | listen_addresses was changed | Put back listen_addresses = 'localhost' and restart |
FATAL: data directory has wrong ownership | PGDATA does not belong to postgres | chown -R postgres:postgres /var/lib/postgresql/18/main |
could not bind ... Address already in use | Another process holds port 5432 | ss -tlnp | grep 5432 to identify it |
| No cluster, no log (Rocky/RHEL) | The cluster was not initialised | postgresql-18-setup initdb |
To understand a service that won't start, read its journal with journalctl: sudo journalctl -u postgresql@18-main --no-pager -n 30 on Debian, sudo journalctl -u postgresql-18 --no-pager -n 30 on Rocky and RHEL. If two versions coexist, pg_lsclusters shows them on two lines; each must have its own port.
Key points
- Debian's
postgresqlpackage is not the version you want (17 on Debian 13): version 18 comes from the PGDG repository. - A third-party repository is declared with
Signed-Bypointing at a key stored in/etc/apt/keyrings/, never withtrusted=yes. - Verify the fingerprint of the key with
gpg --show-keys --with-fingerprintbefore trusting it. - On Debian,
apt install postgresql-18creates and starts the18/maincluster; on Rocky and RHEL, you needinitdbthensystemctl enable --now. - An active service is not enabled: check
systemctl is-enabled postgresqland theautovalue ofstart.conf, or really reboot. - Checksums are enabled by default in 18: confirm it with
SHOW data_checksumsandpg_controldata. - A fresh server listens only on
localhost: check that a listener exists, then that it is local.
Knowledge check
Eight questions on the origin of packages, the signing key and its fingerprint, the difference between an active and an enabled service, checksums and the network listening of a fresh server.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Administer a PostgreSQL instance with psql
A new full quiz with random questions
Work again only on the questions you missed