Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

By updated

Improve this page

PostgreSQL logo

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 deb822 format, bound to its key by Signed-By
  • Verify the key's fingerprint before trusting it
  • Install postgresql-18 and recognise the 18/main cluster 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 sudo access.
  • The basics of APT, explained in Manage packages with APT, including the Signed-By field.
  • The cluster, instance and PGDATA vocabulary from Découvrir PostgreSQL (in French).
  • An Internet connection to apt.postgresql.org and www.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.

  1. 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, and curl -fsSL makes the download fail on an HTTP error:

    Terminal window
    sudo apt-get install -y curl ca-certificates gnupg
    sudo install -d -m 0755 /etc/apt/keyrings
    sudo curl -fsSL -o /etc/apt/keyrings/pgdg.asc \
    https://www.postgresql.org/media/keys/ACCC4CF8.asc
  2. 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.asc

    The fingerprint of the PostgreSQL repository key (identity PostgreSQL Debian Repository, RSA 4096) is B97B 0AFC AA1A 47F0 44F2 44A0 7FCC 7D46 ACCC 4CF8. If it differs, delete the file and do not go on.

  3. Declare the repository, bound to the key

    The deb822 format stores the repository in a .sources file. The Signed-By field points at the key above; Suites holds your Debian codename followed by -pgdg:

    Terminal window
    sudo tee /etc/apt/sources.list.d/pgdg.sources > /dev/null <<'EOF'
    Types: deb
    URIs: https://apt.postgresql.org/pub/repos/apt
    Suites: trixie-pgdg
    Components: main
    Signed-By: /etc/apt/keyrings/pgdg.asc
    EOF

    On Debian 12, replace trixie-pgdg with bookworm-pgdg. Your machine's codename is in /etc/os-release (VERSION_CODENAME).

  4. Refresh the index and check

    Terminal window
    sudo apt-get update

    The output must mention apt.postgresql.org without NO_PUBKEY, not signed or 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.

Terminal window
sudo apt-get install -y postgresql-18

The installation pulls in its dependencies: postgresql-client-18, postgresql-18-jit, postgresql-common and postgresql-client-common. Now check what you got:

Terminal window
pg_lsclusters
Ver Cluster Port Status Owner Data directory Log file
18 main 5432 online postgres /var/lib/postgresql/18/main /var/log/postgresql/postgresql-18-main.log

The 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:

Terminal window
dpkg -l 'postgresql*' | grep '^ii'
ii postgresql-18 18.6-1.pgdg13+2 amd64 The World's Most Advanced Open Source Relational Database
ii postgresql-18-jit 18.6-1.pgdg13+2 amd64 LLVM JIT support for PostgreSQL 18
ii postgresql-client-18 18.6-1.pgdg13+2 amd64 front-end programs for PostgreSQL 18
ii postgresql-client-common 293.pgdg13+1 all manager for multiple PostgreSQL client versions
ii postgresql-common 293.pgdg13+1 all PostgreSQL database-cluster manager

The 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.

PackageContents
postgresql-18The PostgreSQL server, initdb, the background workers
postgresql-client-18The client tools: psql, pg_dump, pg_restore, pg_isready
postgresql-commonDebian's multi-cluster manager: pg_lsclusters, pg_ctlcluster, systemd scripts
postgresql-18-jitJIT 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 cluster

The 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:

Terminal window
systemctl is-enabled postgresql
systemctl is-active postgresql@18-main
grep -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:

Terminal window
pg_isready
/var/run/postgresql:5432 - accepting connections

The 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 connections

A 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.

Terminal window
sudo -u postgres psql -X -At -c 'SHOW data_checksums'
on

The cluster's control file, read by pg_controldata, gives the version of the checksum format written to disk:

Terminal window
sudo -u postgres /usr/lib/postgresql/18/bin/pg_controldata /var/lib/postgresql/18/main | grep -i checksum
Data page checksum version: 1

A 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:

Terminal window
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 [::]:*
localhost

The 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.

  1. 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
  2. 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
  3. Install PostgreSQL 18

    Terminal window
    sudo dnf install -y postgresql18-server postgresql18
  4. Initialise the cluster, then start and enable the service

    Terminal window
    sudo /usr/pgsql-18/bin/postgresql-18-setup initdb
    sudo 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.

SymptomLikely causeFix
pg_lsclusters shows version 17You installed Debian's packageDeclare the PGDG repository, then install postgresql-18
NO_PUBKEY or not signed at apt-get updateKey missing, or wrong Signed-By pathDownload the key again into /etc/apt/keyrings/ and fix Signed-By
The instance answers but vanishes at bootActive but not enabledsystemctl is-enabled postgresql, then sudo systemctl enable postgresql
A listener on 0.0.0.0 or *listen_addresses was changedPut back listen_addresses = 'localhost' and restart
FATAL: data directory has wrong ownershipPGDATA does not belong to postgreschown -R postgres:postgres /var/lib/postgresql/18/main
could not bind ... Address already in useAnother process holds port 5432ss -tlnp | grep 5432 to identify it
No cluster, no log (Rocky/RHEL)The cluster was not initialisedpostgresql-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 postgresql package 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-By pointing at a key stored in /etc/apt/keyrings/, never with trusted=yes.
  • Verify the fingerprint of the key with gpg --show-keys --with-fingerprint before trusting it.
  • On Debian, apt install postgresql-18 creates and starts the 18/main cluster; on Rocky and RHEL, you need initdb then systemctl enable --now.
  • An active service is not enabled: check systemctl is-enabled postgresql and the auto value of start.conf, or really reboot.
  • Checksums are enabled by default in 18: confirm it with SHOW data_checksums and pg_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

8 questions
8 min
70% to pass

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

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn