# Previous state - pgbackrest on Railway
# PostgreSQL 16 in Railway container
# Database: ~4.2 GB on disk
# Frequency: daily full backup + continuous WAL archiving
# Tested restore time: 18 minutes (measured, not estimated)
# Last active pgbackrest version: 2.49 (no relevant commits in 8 months)
# Previous state - pgbackrest on Railway
# PostgreSQL 16 in Railway container
# Database: ~4.2 GB on disk
# Frequency: daily full backup + continuous WAL archiving
# Tested restore time: 18 minutes (measured, not estimated)
# Last active pgbackrest version: 2.49 (no relevant commits in 8 months)
# Previous state - pgbackrest on Railway
# PostgreSQL 16 in Railway container
# Database: ~4.2 GB on disk
# Frequency: daily full backup + continuous WAL archiving
# Tested restore time: 18 minutes (measured, not estimated)
# Last active pgbackrest version: 2.49 (no relevant commits in 8 months)
# Base Barman installation (on dedicated server or separate container)
-weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install barman barman-cli # Check version — important for PG16 compatibility
barman --version
# Output: Barman 3.10.0 (confirmed pg16 support)
# Base Barman installation (on dedicated server or separate container)
-weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install barman barman-cli # Check version — important for PG16 compatibility
barman --version
# Output: Barman 3.10.0 (confirmed pg16 support)
# Base Barman installation (on dedicated server or separate container)
-weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install barman barman-cli # Check version — important for PG16 compatibility
barman --version
# Output: Barman 3.10.0 (confirmed pg16 support)
# /etc/barman.conf — base configuration
[barman]
barman_user = barman
configuration_files_directory = /etc/barman.d
barman_home = /var/lib/barman
# Directory where backups go — in my case, Railway persistent volume
log_file = /var/log/barman/barman.log
log_level = INFO
compression = gzip
# This matters: backup_method streaming avoids SSH on Railway
backup_method = streaming
streaming_archiver = on
# /etc/barman.conf — base configuration
[barman]
barman_user = barman
configuration_files_directory = /etc/barman.d
barman_home = /var/lib/barman
# Directory where backups go — in my case, Railway persistent volume
log_file = /var/log/barman/barman.log
log_level = INFO
compression = gzip
# This matters: backup_method streaming avoids SSH on Railway
backup_method = streaming
streaming_archiver = on
# /etc/barman.conf — base configuration
[barman]
barman_user = barman
configuration_files_directory = /etc/barman.d
barman_home = /var/lib/barman
# Directory where backups go — in my case, Railway persistent volume
log_file = /var/log/barman/barman.log
log_level = INFO
compression = gzip
# This matters: backup_method streaming avoids SSH on Railway
backup_method = streaming
streaming_archiver = on
# /etc/barman.d/railway-postgres.conf — specific server configuration
[railway-postgres]
description = "PostgreSQL 16 production Railway"
conninfo = host=<RAILWAY_HOST> user=barman dbname=postgres
streaming_conninfo = host=<RAILWAY_HOST> user=streaming_barman
backup_method = streaming
streaming_archiver = on
slot_name = barman_streaming_slot
# Without this parameter, Barman will complain constantly
streaming_archiver_name = barman_receive_wal
# Retention: 7 full backups or 14 days, whichever comes first
retention_policy = RECOVERY WINDOW OF 14 DAYS
# /etc/barman.d/railway-postgres.conf — specific server configuration
[railway-postgres]
description = "PostgreSQL 16 production Railway"
conninfo = host=<RAILWAY_HOST> user=barman dbname=postgres
streaming_conninfo = host=<RAILWAY_HOST> user=streaming_barman
backup_method = streaming
streaming_archiver = on
slot_name = barman_streaming_slot
# Without this parameter, Barman will complain constantly
streaming_archiver_name = barman_receive_wal
# Retention: 7 full backups or 14 days, whichever comes first
retention_policy = RECOVERY WINDOW OF 14 DAYS
# /etc/barman.d/railway-postgres.conf — specific server configuration
[railway-postgres]
description = "PostgreSQL 16 production Railway"
conninfo = host=<RAILWAY_HOST> user=barman dbname=postgres
streaming_conninfo = host=<RAILWAY_HOST> user=streaming_barman
backup_method = streaming
streaming_archiver = on
slot_name = barman_streaming_slot
# Without this parameter, Barman will complain constantly
streaming_archiver_name = barman_receive_wal
# Retention: 7 full backups or 14 days, whichever comes first
retention_policy = RECOVERY WINDOW OF 14 DAYS
-- In PostgreSQL: create users required by Barman
CREATE USER barman WITH SUPERUSER;
CREATE USER streaming_barman WITH REPLICATION; -- Adjust pg_hba.conf to allow both connections
-- host replication streaming_barman <BARMAN_IP> md5
-- host all barman <BARMAN_IP> md5
-- In PostgreSQL: create users required by Barman
CREATE USER barman WITH SUPERUSER;
CREATE USER streaming_barman WITH REPLICATION; -- Adjust pg_hba.conf to allow both connections
-- host replication streaming_barman <BARMAN_IP> md5
-- host all barman <BARMAN_IP> md5
-- In PostgreSQL: create users required by Barman
CREATE USER barman WITH SUPERUSER;
CREATE USER streaming_barman WITH REPLICATION; -- Adjust pg_hba.conf to allow both connections
-- host replication streaming_barman <BARMAN_IP> md5
-- host all barman <BARMAN_IP> md5
# Run initial backup
barman backup railway-postgres # Relevant output (measured in my case):
# Starting backup for server railway-postgres
# Backup -weight: 500;">start at LSN: 0/8A000028
# Copying files...
# Backup size: 4.1 GB (vs 4.2 GB with pgbackrest — minimal difference with gzip)
# Elapsed time: 12 minutes 34 seconds
# Backup end at LSN: 0/8A001FF8
# Backup completed (-weight: 500;">start time: ..., elapsed time: 12 minutes, 34 seconds)
# Run initial backup
barman backup railway-postgres # Relevant output (measured in my case):
# Starting backup for server railway-postgres
# Backup -weight: 500;">start at LSN: 0/8A000028
# Copying files...
# Backup size: 4.1 GB (vs 4.2 GB with pgbackrest — minimal difference with gzip)
# Elapsed time: 12 minutes 34 seconds
# Backup end at LSN: 0/8A001FF8
# Backup completed (-weight: 500;">start time: ..., elapsed time: 12 minutes, 34 seconds)
# Run initial backup
barman backup railway-postgres # Relevant output (measured in my case):
# Starting backup for server railway-postgres
# Backup -weight: 500;">start at LSN: 0/8A000028
# Copying files...
# Backup size: 4.1 GB (vs 4.2 GB with pgbackrest — minimal difference with gzip)
# Elapsed time: 12 minutes 34 seconds
# Backup end at LSN: 0/8A001FF8
# Backup completed (-weight: 500;">start time: ..., elapsed time: 12 minutes, 34 seconds)
# Test restore to staging server
barman recover railway-postgres latest /tmp/postgres-restore-test \ --target-time "2025-07-14 10:00:00" \ --remote-ssh-command "ssh postgres@staging" # Measured time: 23 minutes 17 seconds
# (vs 18 minutes with pgbackrest — 5 minutes slower)
# Test restore to staging server
barman recover railway-postgres latest /tmp/postgres-restore-test \ --target-time "2025-07-14 10:00:00" \ --remote-ssh-command "ssh postgres@staging" # Measured time: 23 minutes 17 seconds
# (vs 18 minutes with pgbackrest — 5 minutes slower)
# Test restore to staging server
barman recover railway-postgres latest /tmp/postgres-restore-test \ --target-time "2025-07-14 10:00:00" \ --remote-ssh-command "ssh postgres@staging" # Measured time: 23 minutes 17 seconds
# (vs 18 minutes with pgbackrest — 5 minutes slower)
# Monitor replication slot usage (run this on your Postgres)
SELECT slot_name, active, restart_lsn, confirmed_flush_lsn, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag
FROM pg_replication_slots
WHERE slot_name = 'barman_streaming_slot'; # In my case during the first 24 hours:
# slot_name | active | lag
# barman_streaming_slot | t | 2.3 MB <- normal
# After a Barman container -weight: 500;">restart:
# barman_streaming_slot | f | 847 MB <- accumulated while Barman was down
# Monitor replication slot usage (run this on your Postgres)
SELECT slot_name, active, restart_lsn, confirmed_flush_lsn, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag
FROM pg_replication_slots
WHERE slot_name = 'barman_streaming_slot'; # In my case during the first 24 hours:
# slot_name | active | lag
# barman_streaming_slot | t | 2.3 MB <- normal
# After a Barman container -weight: 500;">restart:
# barman_streaming_slot | f | 847 MB <- accumulated while Barman was down
# Monitor replication slot usage (run this on your Postgres)
SELECT slot_name, active, restart_lsn, confirmed_flush_lsn, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag
FROM pg_replication_slots
WHERE slot_name = 'barman_streaming_slot'; # In my case during the first 24 hours:
# slot_name | active | lag
# barman_streaming_slot | t | 2.3 MB <- normal
# After a Barman container -weight: 500;">restart:
# barman_streaming_slot | f | 847 MB <- accumulated while Barman was down
barman check railway-postgres # Misleading output:
# Server railway-postgres:
# PostgreSQL: OK
# is_superuser: OK
# PostgreSQL streaming: OK
# wal_level: OK
# replication slot: OK
# directories: OK
# retention policy settings: OK
# backup maximum age: FAILED (no target...) <- this error is actually a warning
# encryption: OK (disabled)
# backup minimum size: OK
# wal compression: OK
# ssh: FAILED (SSH connection is not active) <- expected with streaming, but check fails anyway
# FAILED (see log for details)
barman check railway-postgres # Misleading output:
# Server railway-postgres:
# PostgreSQL: OK
# is_superuser: OK
# PostgreSQL streaming: OK
# wal_level: OK
# replication slot: OK
# directories: OK
# retention policy settings: OK
# backup maximum age: FAILED (no target...) <- this error is actually a warning
# encryption: OK (disabled)
# backup minimum size: OK
# wal compression: OK
# ssh: FAILED (SSH connection is not active) <- expected with streaming, but check fails anyway
# FAILED (see log for details)
barman check railway-postgres # Misleading output:
# Server railway-postgres:
# PostgreSQL: OK
# is_superuser: OK
# PostgreSQL streaming: OK
# wal_level: OK
# replication slot: OK
# directories: OK
# retention policy settings: OK
# backup maximum age: FAILED (no target...) <- this error is actually a warning
# encryption: OK (disabled)
# backup minimum size: OK
# wal compression: OK
# ssh: FAILED (SSH connection is not active) <- expected with streaming, but check fails anyway
# FAILED (see log for details)