# Replication Setup in Postgres Using Docker

## Setting Up PostgreSQL Master-Replica Architecture with Docker <a href="#setting-up-postgresql-master-replica-architecture" id="setting-up-postgresql-master-replica-architecture"></a>

&#x20;Here's a comprehensive step-by-step guide to set up a PostgreSQL master-replica cluster with one master and three replica nodes.\
We will use postgres **asynchronous replication** for master and replica

### Project Structure Setup <a href="#project-structure-setup" id="project-structure-setup"></a>

First, ensure your project directory follows this structure:

```
postgres-replica-demo/
├── docker-compose.yml
├── master/
│   ├── Dockerfile
│   └── init-master.sh
└── replica/
    ├── Dockerfile
    └── docker-entrypoint.sh
```

### Step 1: Complete the Master Configuration Files <a href="#step-1-complete-the-master-configuration-files" id="step-1-complete-the-master-configuration-files"></a>

### Master Dockerfile

Dockerfile for master database docker instance , it will create image and added sh script as entry point to mark it for master for replication.

&#x20;`./master/Dockerfile`:

```docker
FROM postgres:16

COPY init-master.sh /docker-entrypoint-initdb.d/init-master.sh

RUN chmod +x /docker-entrypoint-initdb.d/init-master.sh
```

### Master Initialization Script

Here is the bash script that will set replication in master database instance

&#x20; `./master/init-master.sh`&#x20;

```bash
#!/bin/bash

set -e

echo "host replication replica_user 0.0.0.0/0 md5" >> "$PGDATA/pg_hba.conf"

cat >> "$PGDATA/postgresql.conf" << EOF
# Replication settings
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
synchronous_commit = off

# Performance settings
shared_buffers = 256MB
checkpoint_completion_target = 0.7
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_cache_size = 512MB
EOF

# Create replication user
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    CREATE USER replica_user REPLICATION LOGIN ENCRYPTED PASSWORD 'root';
EOSQL
```

**Key Configuration Explanations:**

* **wal\_level = replica**: Enables write-ahead logging for replication
* **max\_wal\_senders = 10**: Allows up to 10 concurrent replica connections
* **max\_replication\_slots = 10**: Creates slots for tracking replication progress
* **synchronous\_commit = off**: Improves performance by not waiting for replica confirmation

### Step 2: Complete the Replica Configuration Files <a href="#step-2-complete-the-replica-configuration-files" id="step-2-complete-the-replica-configuration-files"></a>

### Replica Dockerfile

Replica container docker file, will create postgres container and run script to mark it as replica.

&#x20;`./replica/Dockerfile`:

```docker
FROM postgres:16

RUN apt-get update && \
    apt-get install -y gosu && \
    rm -rf /var/lib/apt/lists/*

COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

RUN chmod +x /usr/local/bin/docker-entrypoint.sh

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
```

### Replica Entrypoint Script

Replication script that handles the replica initialization:

`./replica/docker-entrypoint.sh`&#x20;

```bash
#!/bin/bash

set -e

# Ensure proper ownership
chown -R postgres:postgres "$PGDATA"
chmod 700 "$PGDATA"

# Wait until master is ready
echo "Waiting for master to be ready..."
until pg_isready -h master -p 5432 -U replica_user; do
    echo "Still waiting for master..."
    sleep 2
done

# Initialize replica if needed
if [ ! -s "$PGDATA/PG_VERSION" ]; then
    echo "Running base backup..."
    gosu postgres pg_basebackup -h master -U replica_user -D "$PGDATA" -Fp -Xs -P -R
    chmod 700 "$PGDATA"
fi

# Start postgres as the correct user
exec gosu postgres postgres
```

**Critical Script Components:**

* **pg\_isready check**: Ensures master is accepting connections before proceeding
* **pg\_basebackup**: Creates a physical copy of the master database
* **-R flag**: Automatically configures replication settings
* **gosu**: Ensures PostgreSQL runs with correct user permissions

### Step 3: Docker -compose file <a href="#step-3-deploy-the-cluster" id="step-3-deploy-the-cluster"></a>

The docker-compose file will be responsible for setting infra.

1. creates master postgres instance running on port 6000
2. creates 3 replica postgres instance running on port 6001, 6002, 6003
3. Every container has volume mount so that data is not lost in case of container shut down.

```docker
version: '3.8'

services:
  master:
    build: ./master
    container_name: master
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: root
    ports:
      - "6000:5432"
    volumes:
      - master-data:/var/lib/postgresql/data
    networks:
      - pgnet

  replica1:
    build: ./replica
    container_name: replica1
    depends_on:
      - master
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: root      # required by postgres image but not used for init
      PGPASSWORD: root             # for pg_basebackup auth replication user
      REPLICA_NAME: replica1
    ports:
      - "6001:5432"
    volumes:
      - replica1-data:/var/lib/postgresql/data
    networks:
      - pgnet

  replica2:
    build: ./replica
    container_name: replica2
    depends_on:
      - master
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: root
      PGPASSWORD: root
      REPLICA_NAME: replica2
    ports:
      - "6002:5432"
    volumes:
      - replica2-data:/var/lib/postgresql/data
    networks:
      - pgnet

  replica3:
    build: ./replica
    container_name: replica3
    depends_on:
      - master
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: root
      PGPASSWORD: root
      REPLICA_NAME: replica3
    ports:
      - "6003:5432"
    volumes:
      - replica3-data:/var/lib/postgresql/data
    networks:
      - pgnet

volumes:
  master-data:
  replica1-data:
  replica2-data:
  replica3-data:

networks:
  pgnet:


```

### &#x20;<a href="#step-3-deploy-the-cluster" id="step-3-deploy-the-cluster"></a>

### Step 4: Deploy the Cluster <a href="#step-3-deploy-the-cluster" id="step-3-deploy-the-cluster"></a>

### Build and Start Services

```bash
# Navigate to your project directory
cd postgres-replica-demo

# Build and start all services
docker-compose up --build -d

# Monitor the startup process
docker-compose logs -f
```

### Verify Container Status

```bash
# Check all containers are running
docker-compose ps

# Expected output:
# NAME       COMMAND                  SERVICE    STATUS         PORTS
# master     "docker-entrypoint.s…"   master     Up 2 minutes   0.0.0.0:6000->5432/tcp
# replica1   "/usr/local/bin/dock…"   replica1   Up 2 minutes   0.0.0.0:6001->5432/tcp
# replica2   "/usr/local/bin/dock…"   replica2   Up 2 minutes   0.0.0.0:6002->5432/tcp
# replica3   "/usr/local/bin/dock…"   replica3   Up 2 minutes   0.0.0.0:6003->5432/tcp
```

### Step 5: Verification and Testing <a href="#step-4-verification-and-testing" id="step-4-verification-and-testing"></a>

### Test Master Database Connection

```bash
# Connect to master
docker exec -it master psql -U postgres

# Create a test database and table
CREATE DATABASE testdb;
\c testdb;
CREATE TABLE users (id SERIAL PRIMARY KEY, name VARCHAR(50), email VARCHAR(100));
INSERT INTO users (name, email) VALUES ('John Doe', 'john@example.com');
```

### Verify Replication Status on Master

```sql
sql-- Check replication status
SELECT client_addr, state, sync_state FROM pg_stat_replication;

-- Expected output showing 3 connected replicas:
-- client_addr | state     | sync_state 
-- 172.x.x.x   | streaming | async
-- 172.x.x.x   | streaming | async  
-- 172.x.x.x   | streaming | async
```

### Test Read Operations on Replicas

```bash
# Connect to replica1
docker exec -it replica1 psql -U postgres -d testdb

# Verify data replication
SELECT * FROM users;

# Should show the inserted data from master
```

### Test Replication Lag

```bash
# On master, check current WAL location
docker exec -it master psql -U postgres -c "SELECT pg_current_wal_lsn();"

# On replica, check received WAL location  
docker exec -it replica1 psql -U postgres -c "SELECT pg_last_wal_receive_lsn();"
```

### Step 6: Performance Monitoring and Health Checks <a href="#step-5-performance-monitoring-and-health-checks" id="step-5-performance-monitoring-and-health-checks"></a>

### Create Monitoring Scripts

Create a `monitor.sh` script for ongoing health checks:

```bash
#!/bin/bash

echo "=== Master Status ==="
docker exec master psql -U postgres -c "SELECT client_addr, application_name, state, sync_state FROM pg_stat_replication;"

echo -e "\n=== Replica Status ==="
for replica in replica1 replica2 replica3; do
    echo "--- $replica ---"
    docker exec $replica psql -U postgres -c "SELECT pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();"
done

echo -e "\n=== Container Resources ==="
docker stats --no-stream master replica1 replica2 replica3
```

### Troubleshooting Common Issues <a href="#troubleshooting-common-issues" id="troubleshooting-common-issues"></a>

### Replica Startup Failures

```bash
# Check replica logs for connection issues
docker-compose logs replica1

# Common solutions:
# 1. Ensure master is fully started
# 2. Verify network connectivity
# 3. Check replication user permissions
```

### Replication Lag Issues

```bash
# Monitor WAL sender processes
docker exec master psql -U postgres -c "SELECT * FROM pg_stat_replication;"

# Check for slow replicas
docker exec master psql -U postgres -c "SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;"
```

### Data Directory Persistence

Your configuration uses named volumes for data persistence:

* **master-data**: Stores master database files
* **replica1-data, replica2-data, replica3-data**: Store replica-specific files

To reset the entire cluster:

```bash
# Stop and remove containers
docker-compose down

# Remove all volumes (WARNING: This deletes all data)
docker-compose down -v

# Restart fresh
docker-compose up --build -d
```

This setup provides a robust PostgreSQL master-replica architecture that's perfect for Spring Boot read replica demo, with proper replication monitoring, health checks, and scalability options.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wisdom.gitbook.io/gyan/core/spring-boot-with-read-replica/replication-setup-in-postgres-using-docker.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
