Right, let’s get your standby server listening to the primary. This isn’t just about copying files; it’s about creating a hotline between the two. The secret handshake for this connection is defined in two places: the primary_conninfo string on the standby and the recovery.conf (or postgresql.conf in v12+) file that tells a server “hey, you’re not the main character in this story.”

First, a moment of silence for recovery.conf. In PostgreSQL 12, the designers, in their infinite wisdom, decided to merge its parameters into postgresql.conf. It’s a cleaner approach long-term, but it means you need to know which version you’re on. I’ll show you both, because I’m a brilliant friend and that’s what we do.

The Anatomy of primary_conninfo

This parameter is the entire connection string the standby uses to find the primary, authenticate, and start begging for WAL records. It’s just a libpq connection string, like you’d use with psql, but with a few critical, replication-specific twists.

Here’s what a robust one looks like. Don’t just copy this; understand it.

primary_conninfo = 'host=primary-server.example.com port=5432 user=replicator password=SuperSecretPass application_name=my-standby-1'

Let’s break this down:

  • host: The DNS name or IP of your primary server. Obvious, but the most common failure point is DNS not resolving from the standby.
  • port: The port the primary is listening on. Double-check your pg_hba.conf on the primary allows connections on this port.
  • user: This is a dedicated replication user, not your superuser. You must create this on the primary. Its only privilege is being REPLICATION role (which is a fancy way of saying it’s allowed to ask for the WAL stream).
  • password: The password for said user. Yes, you need to put it in plain text here. Before you panic, the recovery.conf (or postgresql.conf) file should be owned by the postgres user and have permissions like 600. It’s a calculated risk.
  • application_name: This is your best friend for monitoring. It shows up in the pg_stat_replication view on the primary, so you can instantly see which standby is which. Naming it my-standby-1 is better than the default, which will just be the walreceiver process ID. Be descriptive.

You can add other libpq parameters here, like sslmode=require to encrypt the stream, which you absolutely should do in any non-toy environment.

Where This All Lives: recovery.conf vs. The New World

If you’re on PostgreSQL 11 or earlier, this primary_conninfo string goes in a file called recovery.conf in the standby’s data directory (PGDATA). This file is the sacred text that triggers standby mode on startup.

PostgreSQL 11 and earlier (recovery.conf): Create this file on your standby server:

# On the standby, with PGDATA owned by postgres user
sudo -u postgres bash -c "cat > /var/lib/postgresql/11/main/recovery.conf << EOF
standby_mode = 'on'
primary_conninfo = 'host=primary-server.example.com port=5432 user=replicator password=SuperSecretPass application_name=my-standby-1'
trigger_file = '/tmp/promote_me_now'
EOF"

See that standby_mode = 'on'? That’s the magic switch. The trigger_file is a cool feature: if that file appears on the standby’s filesystem, the standby will promote itself to a primary. It’s a much cleaner signal than pg_ctl promote.

PostgreSQL 12 and later (postgresql.conf): The paradigm shifted. Now, you set these parameters directly in postgresql.conf and use a standby.signal file to trigger the mode.

# On the standby, add to postgresql.conf
sudo -u postgres bash -c "echo \"primary_conninfo = 'host=primary-server.example.com port=5432 user=replicator password=SuperSecretPass application_name=my-standby-1'\" >> /var/lib/postgresql/12/main/postgresql.conf"

# Then, create the empty signal file
sudo -u postgres touch /var/lib/postgresql/12/main/standby.signal

The logic is cleaner—configuration in one place, a simple signal file to define the mode—but it’s a headache for anyone managing cross-version environments.

The Primary’s pg_hba.conf: The Bouncer

This is the pitfall. You can configure your standby perfectly, but if the primary’s pg_hba.conf file doesn’t allow the connection, it will fail with a frustratingly generic “connection refused” error. The primary is the bouncer; it needs to be told the replicator is on the list.

On the primary server, add a line like this to pg_hba.conf:

# TYPE  DATABASE        USER        ADDRESS                 METHOD
host    replication     replicator  192.168.1.100/32        scram-sha-256

This line is crucial: it allows the replicator user to connect from the standby’s IP (192.168.1.100) to the special replication database (which isn’t a real database, just a keyword for this purpose) using SCRAM-SHA-256 password authentication. Forget to do this, and you’ll be scratching your head for an hour. After changing pg_hba.conf, you must reload the primary (pg_ctl reload or SELECT pg_reload_conf();) for the change to take effect.

Once it’s all set, start the standby. If the stars are aligned, you’ll see a walreceiver process on the standby and, crucially, a walsender process on the primary when you check pg_stat_replication. That’s when you know the hotline is open.