Set up PostgreSQL replication on Ubuntu by configuring a writable primary, creating a narrowly scoped replication login, taking a consistent pg_basebackup, and starting a matching standby with streaming enabled. PostgreSQL calls this architecture primary–standby; “master–slave” is older terminology retained here so the procedure is easy to find.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
The default is asynchronous streaming: the primary can acknowledge a commit before the standby receives it. That keeps write latency lower, but a primary failure can lose transactions that had not reached the standby. Synchronous replication reduces that exposure by making commits wait for a configured standby, at the cost of latency and write availability when that standby is unavailable.
As an Amazon Associate I earn from qualifying purchases.
What this setup provides—and what it does not
Physical streaming replication copies the primary’s write-ahead log (WAL) to another PostgreSQL server, where the WAL is received and replayed. The standby is initialized from a consistent copy of the entire cluster, normally with pg_basebackup, and remains in recovery mode until you deliberately promote it.
- There must be one writable primary and at least one standby with network access to PostgreSQL’s port.
- The servers need the same PostgreSQL major version, compatible authentication, sufficient disk, and synchronized clocks.
- Replication is not an independent backup. A mistaken promotion, corruption, or operator error can be reproduced on the standby, so retain separate backups and test point-in-time recovery.
- Replication alone does not provide automatic leader election, client redirection, fencing, split-brain prevention, or a tested disaster-recovery runbook.
Use this guide for physical replication between two Ubuntu hosts. Substitute the installed major version everywhere a path contains <major>; Ubuntu package layouts and service names can differ by release, so verify them before editing files.
#1 Best Overall
Plan the two-host deployment
Primary and standby roles
| Host | Role | Typical responsibilities |
|---|---|---|
| Primary | Writable server | Accepts application writes, generates WAL, and sends WAL to standbys. |
| Standby | Receiving server | Streams and replays WAL, serves read-only queries when hot standby is enabled, and can be promoted during a recovery event. |
Prerequisites checklist
- Both hosts can resolve each other or use fixed addresses, and the standby can reach the primary on TCP port 5432 (or your configured PostgreSQL port).
- The standby has enough free space for a full copy of the primary data directory plus future WAL retention.
- Both installations use the same PostgreSQL major version. Check with
psql --versionon each host. - System clocks are synchronized; timestamps are useful when diagnosing lag and failover.
- You have protected administrative access and a plan for storing the replication credential.
- Firewall rules permit the PostgreSQL port only from the standby address, not from the entire internet.
Install and identify the PostgreSQL instance
Install the same major version on both Ubuntu hosts. If the distribution repository’s default is acceptable, use:
sudo apt update
sudo apt install postgresql postgresql-contrib
When you need a specific major version, use the corresponding versioned packages instead:
sudo apt install postgresql-<major> postgresql-client-<major>
Confirm the installed version and the service that Ubuntu created:
psql --version
systemctl list-units --type=service 'postgresql*'
Do not assume that postgresql is the exact service unit on every installation. The common cluster unit is postgresql@<major>-main, but use the name shown on your host.
Ask PostgreSQL for the authoritative configuration and data paths rather than guessing:
sudo -u postgres psql -Atc 'SHOW config_file;'
sudo -u postgres psql -Atc 'SHOW hba_file;'
sudo -u postgres psql -Atc 'SHOW data_directory;'
The usual Ubuntu paths are /etc/postgresql/<major>/main/ for configuration and /var/lib/postgresql/<major>/main/ for data, but the values returned by PostgreSQL take precedence.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Configure the primary
Set WAL and sender capacity
Edit the primary’s postgresql.conf and set values appropriate for your topology:
| Setting | Purpose | Example and qualification |
|---|---|---|
wal_level |
Enables the WAL detail needed for physical replication. | replica is the normal value for a streaming standby. |
max_wal_senders |
Limits concurrent standby and base-backup connections. | Set it higher than the number of standbys plus concurrent pg_basebackup jobs. PostgreSQL 17 documents a default of 10; do not treat that default as a capacity recommendation. |
max_replication_slots |
Reserves slots for physical or logical replication. | Set at least the number of physical slots you will create, with operational headroom. |
listen_addresses |
Controls which network interfaces accept PostgreSQL connections. | Prefer the primary’s private address, such as 10.0.0.10, instead of exposing every interface. |
wal_level, max_wal_senders, and max_replication_slots are startup parameters and normally require a PostgreSQL restart. Make the change during a maintenance window, then restart the primary with the verified service name.
Create a dedicated replication login
Use a role that can replicate but is not an application superuser. Open an administrative psql session on the primary:
sudo -u postgres psql
Run:
CREATE ROLE replicator WITH LOGIN REPLICATION;
Set its password interactively with password replicator, then exit with q. A long, unique secret should be stored in a protected secret store or a mode-600 .pgpass file on the standby, not in shell history or a world-readable configuration file.
Free tools Windows power users keep installed
One-click scans. No signup required.
Allow only the standby in pg_hba.conf
Add a host rule to the primary’s active HBA file. Replace the example address with the standby’s fixed private address:
host replication replicator 10.0.0.22/32 scram-sha-256
The database field is literally replication; this rule is for the replication protocol rather than ordinary application connections. A single-host /32 CIDR is narrower than an entire subnet. Place the rule where it will be reached before a conflicting reject or broader rule.
Reload after HBA changes:
sudo systemctl reload postgresql
If you changed a startup-only parameter, restart instead. Restrict the host firewall at the same time. With UFW, an example is:
sudo ufw allow from 10.0.0.22 to any port 5432 proto tcp
Use your actual port and network policy; do not copy a broad allow 5432/tcp rule onto an internet-facing host.
Recommended Free Tools
Decide whether to use a physical replication slot
A physical slot records the oldest WAL position still needed by a standby. It prevents the primary from recycling required WAL while that standby is disconnected. Create one on the primary before taking the base backup:
sudo -u postgres psql -c 'SELECT * FROM pg_create_physical_replication_slot(''standby1_slot'');'
The slot name must match the standby’s primary_slot_name. Slots are not free storage: an abandoned or inactive slot can retain WAL until the primary’s pg_wal filesystem fills. Configure and monitor max_slot_wal_keep_size where an upper bound is appropriate, and remove a slot only after confirming that no standby still depends on it.
Initialize the standby with pg_basebackup
Stop PostgreSQL and prepare an empty data directory
Stop the standby cluster before replacing its data directory:
sudo systemctl stop postgresql@<major>-main
Move the old directory aside rather than deleting it immediately, then create an empty directory owned by the PostgreSQL operating-system account. Use the data path returned by SHOW data_directory:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
sudo mv /var/lib/postgresql/<major>/main /var/lib/postgresql/<major>/main.before-replica
sudo install -d -o postgres -g postgres /var/lib/postgresql/<major>/main
Do not run a base backup into a directory containing an unrelated cluster. Keeping the old directory temporarily gives you a recovery option if you mistype a path.
Run the consistent base backup
From the standby host, run pg_basebackup against the primary. The utility copies the complete running cluster over the replication protocol and can create the standby startup files:
sudo -u postgres pg_basebackup -h 10.0.0.10 -p 5432 -U replicator -D /var/lib/postgresql/<major>/main -Fp -Xs -P -R -S standby1_slot
-Fprequests plain files in the target data directory.-Xsstreams WAL during the backup so the copy is consistent without a separate archive-fetch step.-Pdisplays progress.-Rwritesstandby.signaland connection settings for standby startup.-S standby1_slotmakes the backup use the physical slot created on the primary.
When prompted, enter the replication role’s password. If you use a .pgpass file, create it for the postgres OS user, restrict it to mode 600, and use a line containing the primary host, port, the replicator user, and its secret. Do not put a reusable password directly on the command line.
Using the slot during the backup protects the WAL needed between the end of the copy and the standby’s first streaming connection. If the command fails because the slot does not exist or the HBA rule rejects the connection, fix the primary and rerun the backup; do not start the partial directory as a standby.
Configure standby connection and startup
The -R option normally writes primary_conninfo into the copied configuration. Inspect it and ensure it names the primary host, PostgreSQL port, replication user, and an identifying application name. A password can be omitted when .pgpass supplies it:
primary_conninfo = 'host=10.0.0.10 port=5432 user=replicator application_name=standby1'
primary_slot_name = 'standby1_slot'
If you did not use -R, add an empty standby.signal file in the data directory and set these parameters in the active configuration. The slot setting is required only when you intend to use the physical slot; a typo causes the standby to fail to connect to that slot.
Set hot_standby = on if this standby must answer read-only queries while it replays WAL. Hot-standby reads are not a substitute for current primary reads: a query can observe data behind the primary by the amount of replay lag.
Check ownership and permissions after copying, then start the standby:
sudo chown -R postgres:postgres /var/lib/postgresql/<major>/main
sudo systemctl start postgresql@<major>-main
A correctly configured standby remains in recovery mode and does not accept ordinary writes.
Verify that WAL is streaming and replaying
Check the primary
On the primary, inspect the sender view:
sudo -u postgres psql -c "SELECT application_name, client_addr, state, sync_state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;"
For an actively connected standby, the row should show the expected address and state = 'streaming'. sync_state indicates whether the standby is asynchronous or currently eligible for synchronous service. An empty result means the primary has no connected streaming standby.
Check the standby receiver and recovery state
On the standby:
sudo -u postgres psql -c "SELECT status, receive_start_lsn, received_lsn, latest_end_lsn, latest_end_time FROM pg_stat_wal_receiver;"
sudo -u postgres psql -c 'SELECT pg_is_in_recovery();'
The receiver status should be streaming, and pg_is_in_recovery() should return true. Compare receive and replay positions when diagnosing lag; receiving WAL does not prove that queries can already see it because replay may be behind.
Perform an end-to-end test
Create a clearly named test row on the primary, wait briefly, and query it on the standby. Use a disposable table in a test database or remove the row after verification. A successful connection alone is insufficient: the row must become visible after WAL replay. Record the normal delay for your workload so an alert can distinguish ordinary lag from a broken receiver.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Choose asynchronous or synchronous replication
PostgreSQL states that “Streaming replication is asynchronous by default.” In asynchronous mode, a commit can be acknowledged on the primary before the standby receives or replays its WAL. If the primary crashes, transactions still absent from the standby can be lost; the possible amount is the unreplicated lag at the failure, not a fixed number of seconds or transactions.
| Decision axis | Asynchronous streaming | Synchronous streaming |
|---|---|---|
| Commit latency | Usually lower because the client does not wait for a standby acknowledgement. | Higher; commits wait according to the selected synchronous-commit policy. |
| Primary failure | Can lose commits not yet received by the standby. | Configured acknowledgements reduce the acknowledged-data gap; choose the policy carefully. |
| Standby unavailable | Primary writes continue, subject to WAL retention and disk capacity. | Writes can block or fail when the required synchronous standby is unavailable. |
| Read staleness | Standby reads can lag by receive or replay delay. | Durability acknowledgement does not automatically make every standby query current; monitor replay and choose remote_apply when commit acknowledgement must wait for replay. |
| Operational complexity | Simpler initial deployment, but lag and WAL retention still require monitoring. | Requires correct standby naming, availability planning, and explicit latency testing. |
| Suitable workload | Most read scaling and disaster-recovery replicas where a small loss window is acceptable. | Workloads that prioritize stronger commit durability over write availability and latency. |
Configure a synchronous standby
Give the standby an application_name in primary_conninfo, then set on the primary:
synchronous_standby_names = 'FIRST 1 (standby1)'
synchronous_commit = on
FIRST 1 means one currently connected and streaming standby named standby1 must acknowledge the commit. PostgreSQL’s documented rule is that synchronous standbys are names in this list that are connected and streaming in real time. Reload settings that permit reload; restart only when a changed parameter requires it.
The synchronous_commit policy controls what acknowledgement means. on waits for the synchronous standby’s WAL flush; remote_write waits for it to write WAL; remote_apply waits until replay, which provides a stronger visibility relationship but can add more latency. Test the selected policy under a standby outage before using it in production.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchProtect WAL when a standby is disconnected
Replication slots
A physical slot ties WAL retention to a standby’s restart position. It helps a temporarily disconnected standby resume without a new base backup, but a stalled slot can consume all free space in pg_wal. Monitor every slot:
sudo -u postgres psql -c "SELECT slot_name, active, restart_lsn, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal FROM pg_replication_slots WHERE slot_type = 'physical';"
Alert when a slot is inactive for longer than your recovery objective, retained WAL grows unexpectedly, or filesystem free space falls. Set a finite max_slot_wal_keep_size when you prefer a bounded disk risk and are prepared to reinitialize a standby that exceeds the bound.
Best Value
WAL archiving
Archiving copies completed WAL segments to independent storage. It can let a disconnected standby catch up after the primary has recycled older segments, and it is essential to a point-in-time-recovery design. A slot and an archive solve different problems:
| Property | Physical slot | WAL archive |
|---|---|---|
| Disconnected standby recovery | Retains WAL on the primary until the slot advances. | Supplies archived segments from separate storage when configured correctly. |
| Disk-retention risk | Can fill the primary’s WAL filesystem if the slot is abandoned. | Consumes archive storage and needs retention and cleanup policies. |
| Storage location | Primary server’s pg_wal. |
Independent archive destination suitable for recovery operations. |
| Monitoring | Watch slot activity, restart LSN age, and free space. | Watch archive-command failures, missing segments, and archive capacity. |
Do not assume that enabling a slot eliminates the need for independent backups or archiving. Select retention controls that match the longest outage you intend to survive.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteMake monitoring part of normal operation
Check replication continuously rather than only during installation. On the primary, monitor:
- Rows in
pg_stat_replication, especially disconnected receivers and astateother thanstreaming. write_lag,flush_lag, andreplay_lag, interpreting them against the workload’s normal behavior.- WAL distance between the current primary position and each standby’s replay position.
- Replication-slot activity and retained WAL size.
- Archive-command failures if WAL archiving is enabled.
- Free space in the filesystem containing
pg_wal.
On each standby, monitor receiver status, received and replayed LSNs, the timestamp of the last replayed transaction, PostgreSQL logs, and whether read-only health checks still succeed. Alert on a disconnected receiver, steadily increasing replay lag, an inactive slot, failed archiving, or low free space. In a non-production environment, stop the standby and confirm that alerts actually fire and recover when it restarts.
Plan and execute a controlled failover
Replication does not decide when to fail over. Define the operator, application-routing method, fencing procedure, and rebuild process before an outage.
- Confirm that the primary is truly unavailable or deliberately fenced. Never promote while the old primary can still accept writes; that creates split brain.
- Inspect the standby’s receive and replay positions and record the latest known lag.
- Promote the standby with an administrative command such as
SELECT pg_promote();from the standby. Verify thatSELECT pg_is_in_recovery();returns false. - Redirect applications, connection pools, monitoring, and scheduled jobs to the new primary. Replication does not change client connection strings for you.
- Once the old primary is isolated, investigate it without allowing it to become writable beside the promoted server.
- Rebuild the old primary as a new standby from a fresh, consistent base backup. A server that followed the old timeline cannot simply be pointed at the new primary without the appropriate rejoin procedure.
Test promotion, client redirection, fencing, and standby reseeding regularly. Document who performs each action and how you prove that the old primary is no longer accepting writes.
Troubleshoot common failures
“No pg_hba.conf entry”
Check that the rule uses the literal replication database field, the exact replication role, the standby’s actual source address, and an authentication method supported by both servers. Reload the HBA file after editing and verify that you edited the file returned by SHOW hba_file.
“Password authentication failed”
Reset the replication role’s password, verify the username in primary_conninfo, and check the standby’s .pgpass permissions and host, port, database, and user fields. Test a harmless connection from the standby using the same operating-system account that runs PostgreSQL.
The standby never reaches streaming
Check DNS or routing, the primary’s listen_addresses, the firewall, and whether PostgreSQL is listening on the expected port. Then inspect primary_conninfo, the slot name, and PostgreSQL logs on both hosts. A successful base backup followed by a failed startup commonly indicates a typo in connection settings or a missing slot.
“Requested WAL segment has already been removed”
The standby fell behind after the primary recycled WAL that it still needed. If an archive contains the missing segment, restore it through the configured archive procedure. Otherwise, stop the standby and take a new pg_basebackup. Add a correctly named slot or improve archive retention before reconnecting it.
Recommended Free Tools
WAL fills the primary’s disk
Find inactive slots and inspect their retained WAL. Confirm which standby is responsible before dropping a slot; dropping the wrong slot can force that standby to be reinitialized. If a standby is permanently retired, remove its slot and apply an archive and slot-retention policy that prevents recurrence.
The copied directory will not start
Verify matching major versions, PostgreSQL ownership and permissions, the presence of standby.signal, and the data directory used by the service. Do not copy configuration paths from a different Ubuntu cluster blindly. Read the PostgreSQL log for the exact path, timeline, or permission error and correct that issue before retrying.
Quick Recap
Security and maintenance details
- Keep the replication role dedicated to replication and avoid granting it application privileges.
- Limit HBA access to the standby’s address and use SCRAM authentication where supported.
- Protect
.pgpasswith mode 600 and PostgreSQL ownership; do not expose credentials in process listings or shell history. - Use encrypted transport if replication traffic crosses an untrusted network, and manage certificates and verification as part of the host security design.
- Apply compatible PostgreSQL updates to both hosts, test them in staging, and keep enough disk for the largest expected backup and WAL backlog.
- Retain independent backups and periodically perform a point-in-time recovery rehearsal.
Deployment checklist
- Install the same PostgreSQL major version and verify the actual cluster service, configuration file, HBA file, and data directory.
- Ensure private network reachability, synchronized clocks, disk capacity, firewall restrictions, and administrative access.
- Set
wal_level, sender capacity, slot capacity, and a controlled listen address on the primary; restart when required. - Create the dedicated replication role, add the standby-only HBA rule, and reload PostgreSQL.
- Create and monitor a physical slot if your retention design uses one.
- Stop the standby, create an empty owned data directory, and run
pg_basebackupwith-Rand the intended slot. - Protect credentials, confirm
primary_conninfoandprimary_slot_name, enable hot standby if needed, and start the standby. - Verify sender, receiver, recovery, receive, and replay status; perform an end-to-end test row.
- Choose asynchronous or synchronous behavior based on acceptable data loss, latency, and outage behavior.
- Monitor lag, slots, WAL, archiving, free space, and alerts continuously.
- Document fencing, promotion, client routing, and rebuilding the old primary, then rehearse the procedure.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




