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 with- source_db.- docker-compose.yml: Runs PostgreSQL 17 with a publication.
- source-init.sql: Creates the- userstable and- users_pubpublication.
 
- target_service/: The subscriber service with- target_db.- docker-compose.yml: Runs PostgreSQL 17 with a subscription.
- target-init.sql: Sets up the- userstable and subscribes to- source_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