This project demonstrates database-level replication between two PostgreSQL databases using its native publish/subscribe feature—no app logic or external tools like Kafka required! It simulates two services (source_service and target_service) where changes in one database (source_db) automatically sync to another (target_db). Perfect for services needing shared, real-time data without business logic overhead.
- How PostgreSQL’s logical replication works.
- Setting up replication between two databases in Docker.
- Using separate
docker-composefiles for service-style isolation. - Testing real-time data sync without writing code.
- Docker and Docker Compose installed.
- Basic terminal skills.
source_service/: The publisher service withsource_db.docker-compose.yml: Runs PostgreSQL 17 with a publication.source-init.sql: Creates theuserstable andusers_pubpublication.
target_service/: The subscriber service withtarget_db.docker-compose.yml: Runs PostgreSQL 17 with a subscription.target-init.sql: Sets up theuserstable and subscribes tosource_db.
git clone https://github.com/thekubera/postgres-logical-replication
cd postgres-logical-replicationBoth services need to talk over the same network:
docker network create replication_netThe publisher (source_db) comes first:
cd source_service
docker compose up -d- Creates a
userstable with initial data (Alice). - Sets up the
users_pubpublication.
The subscriber (target_db) connects to source_db:
cd ../target_service
docker compose up -d- Creates a
userstable. - Subscribes to
users_pubviausers_sub.
Let’s see replication in action!
In source_db:
docker exec -it source_service-source_db-1 psql -U postgres -d source_db -c "SELECT * FROM users;"Output: Alice (ID 1).
In target_db:
docker exec -it target_service-target_db-1 psql -U postgres -d target_db -c "SELECT * FROM users;"Output: Alice (synced!).
Insert a row in source_db:
docker exec -it source_service-source_db-1 psql -U postgres -d source_db -c "INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');"Check target_db:
docker exec -it target_service-target_db-1 psql -U postgres -d target_db -c "SELECT * FROM users;"Output: Alice and Bob—magic!
Update in source_db:
docker exec -it source_service-source_db-1 psql -U postgres -d source_db -c "UPDATE users SET name = 'Bob Updated' WHERE email = 'bob@example.com';"Check target_db:
docker exec -it target_service-target_db-1 psql -U postgres -d target_db -c "SELECT * FROM users;"Output: Alice and Bob Updated.
- Publisher (
source_db): Useswal_level=logicaland a publication (users_pub) to stream changes from theuserstable. - Subscriber (
target_db): Uses a subscription (users_sub) to pull changes fromsource_dbover thereplication_netnetwork. - Docker: Runs each service in isolated containers, mimicking services on different servers.
- “Could not translate host name”: Ensure both services are on
replication_net(docker network inspect replication_net). - “Max WAL senders” error:
max_wal_sendersandmax_replication_slotsare set to 2 insource_serviceto handle initial sync and ongoing replication. - No data in
target_db: Check logs (docker compose logs) and subscription status:
docker exec -it target_service-target_db-1 psql -U postgres -d target_db -c "SELECT * FROM pg_stat_subscription;"cd source_service
docker compose down
cd ../target_service
docker compose down