34.2 Streaming Replication Setup: primary_conninfo and recovery.conf
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 yourpg_hba.confon 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 beingREPLICATIONrole (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, therecovery.conf(orpostgresql.conf) file should be owned by thepostgresuser and have permissions like600. It’s a calculated risk.application_name: This is your best friend for monitoring. It shows up in thepg_stat_replicationview on the primary, so you can instantly see which standby is which. Naming itmy-standby-1is 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.