back to docs
testnet

Docker Method

Setup Ethereum Sepolia Beacon Chain using Docker + Nginx + Certbot

This guide walks you through setting up your own Ethereum Sepolia testnet node with both execution and consensus layers. You will:

  1. Install Docker, Nginx, and Certbot
  2. Configure Geth (execution) and Prysm (consensus) clients
  3. Set up reverse proxy with SSL
  4. Monitor and maintain your node

This setup provides you with your own dedicated RPC endpoints for Sepolia (ETH & Beacon).

Important
This is NOT a copy-paste guide. Please read carefully and adjust values for your environment!

1. Hardware Requirements

ComponentRequirementRecommended
CPU4 Core8 Core
RAM16 GB32 GB
Storage1 TB SSD2 TB NVMe SSD
Network10 Mbps stable25+ Mbps
Note
Sepolia testnet requires significant storage and memory. Monitor resource usage regularly.
Server Recommendations

Check our Server Provider List for recommended options.

2. Installation

a. Install Dependencies

Ensure you have the required software installed:

Install Nginx:

sudo apt update -qy
sudo apt install nginx -y

Start and enable Nginx:

sudo systemctl start nginx
sudo systemctl enable nginx

Check status:

sudo systemctl status nginx

Install Certbot for SSL:

sudo apt install certbot python3-certbot-nginx -y

b. Setup Ethereum Node

Create project directory:

mkdir sepolia-rpc && cd sepolia-rpc

Create Docker Compose file:

nano docker-compose.yml

Add Docker Compose Configuration:

services:
  geth:
    image: ethereum/client-go:v1.15.11
    container_name: geth-sepolia
    restart: unless-stopped
    ports:
      - "127.0.0.1:8545:8545"    # HTTP RPC (localhost only)
      - "30303:30303"            # P2P TCP
      - "30303:30303/udp"        # P2P UDP
    volumes:
      - ./data/geth-sepolia:/root/.ethereum
      - ./data/jwt.hex:/root/jwt.hex
    command: >
      --sepolia
      --http
      --http.addr "0.0.0.0"
      --http.port 8545
      --http.api "eth,net,web3"
      --http.corsdomain "*"
      --http.vhosts "*"
      --authrpc.addr "0.0.0.0"
      --authrpc.port 8551
      --authrpc.vhosts "*"
      --authrpc.jwtsecret /root/jwt.hex
      --cache 2048
      --syncmode snap
    deploy:
      resources:
        limits:
          memory: 16G
        reservations:
          memory: 8G
    networks:
      - sepolia-network

  prysm:
    image: gcr.io/prysmaticlabs/prysm/beacon-chain:v6.0.1
    container_name: prysm-sepolia
    restart: unless-stopped
    depends_on:
      - geth
    ports:
      - "127.0.0.1:3500:3500"  # HTTP API (localhost only)
      - "127.0.0.1:4000:4000"  # RPC (localhost only)
      - "13000:13000"          # P2P TCP
      - "12000:12000/udp"      # P2P UDP
    volumes:
      - ./data/prysm:/data
      - ./data/jwt.hex:/root/jwt.hex
    command: >
      --datadir=/data
      --sepolia
      --execution-endpoint=http://geth:8551
      --rpc-host=0.0.0.0
      --rpc-port=4000
      --grpc-gateway-host=0.0.0.0
      --grpc-gateway-port=3500
      --jwt-secret=/root/jwt.hex
      --genesis-beacon-api-url=https://sepolia.checkpoint-sync.ethpandaops.io
      --checkpoint-sync-url=https://sepolia.checkpoint-sync.ethpandaops.io
      --accept-terms-of-use
      --http-modules=beacon,config,node
      --min-sync-peers=1
    deploy:
      resources:
        limits:
          memory: 10G
        reservations:
          memory: 6G
    networks:
      - sepolia-network

networks:
  sepolia-network:
    name: sepolia-network
    driver: bridge

volumes:
  geth-sepolia:
  prysm:

c. Initialize and Start Services

Create the JWT secret file:

mkdir -p data
openssl rand -hex 32 > data/jwt.hex

Start the services:

docker compose up -d

View Geth logs:

docker logs -f geth-sepolia

View Prysm logs:

docker logs -f prysm-sepolia

Monitor both services:

docker compose logs -f

3. Nginx Reverse Proxy Setup

a. Configure Nginx for Geth (Execution Layer)

Create Nginx configuration for Geth:

sudo nano /etc/nginx/sites-available/sepolia-eth-rpc

Add the following configuration:

server {
    server_name eth-sepolia.yourdomain.com;

    access_log /var/log/nginx/sepolia_eth_rpc_access.log;
    error_log /var/log/nginx/sepolia_eth_rpc_error.log;

    location / {
        # IP whitelist - replace with your actual IPs
        allow 172.16.0.0/12;     # Docker networks
        allow 10.0.0.0/8;        # Private networks
        allow 192.168.0.0/16;    # Private networks
        allow 203.0.113.1;       # Your specific IP
        allow 198.51.100.0/24;   # Your network range

        # Deny all other traffic
        deny all;

        # Proxy configuration
        proxy_pass http://127.0.0.1:8545;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;

        # Timeout settings
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Request size limits
        client_max_body_size 1M;

        # Rate limiting (optional)
        limit_req zone=api burst=10 nodelay;
    }
}

b. Configure Nginx for Prysm (Consensus Layer)

Create Nginx configuration for Prysm:

sudo nano /etc/nginx/sites-available/sepolia-beacon-rpc

Add the following configuration:

server {
    server_name beacon-sepolia.yourdomain.com;

    access_log /var/log/nginx/sepolia_beacon_rpc_access.log;
    error_log /var/log/nginx/sepolia_beacon_rpc_error.log;

    location / {
        # IP whitelist - same as above
        allow 172.16.0.0/12;
        allow 10.0.0.0/8;
        allow 192.168.0.0/16;
        allow 203.0.113.1;
        allow 198.51.100.0/24;

        deny all;

        # Proxy to Prysm HTTP API
        proxy_pass http://127.0.0.1:3500;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;

        # Timeout settings
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        client_max_body_size 1M;
        limit_req zone=api burst=10 nodelay;
    }
}

c. Enable Nginx Sites

Add rate limiting to main Nginx config:

sudo nano /etc/nginx/nginx.conf

Add this inside the http block:

# Rate limiting zone
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

Create symbolic links:

sudo ln -s /etc/nginx/sites-available/sepolia-eth-rpc /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/sepolia-beacon-rpc /etc/nginx/sites-enabled/

Test configuration:

sudo nginx -t

Reload Nginx:

sudo systemctl reload nginx

d. Setup SSL with Certbot

Obtain SSL certificate for Geth endpoint:

sudo certbot --nginx -d eth-sepolia.yourdomain.com

Obtain SSL certificate for Prysm endpoint:

sudo certbot --nginx -d beacon-sepolia.yourdomain.com

Follow the prompts to configure SSL.

Test certificate renewal:

sudo certbot renew --dry-run

4. Service Configuration Details

a. Geth (Execution Client)

  • Image: ethereum/client-go:v1.15.11
  • Network: Sepolia testnet
  • Sync Mode: Snap sync (faster initial sync)
  • RPC Endpoint: https://eth-sepolia.yourdomain.com
  • Available APIs: eth, net, web3
  • Memory: 8-16GB allocated

b. Prysm (Consensus Client)

  • Image: gcr.io/prysmaticlabs/prysm/beacon-chain:v6.0.1
  • Network: Sepolia testnet
  • HTTP API: https://beacon-sepolia.yourdomain.com
  • RPC: http://localhost:4000 (internal only)
  • Memory: 6-10GB allocated
  • Checkpoint Sync: Enabled for faster sync

c. Port Configuration

ServicePortProtocolPurposeAccess
Geth8545HTTPJSON-RPC APILocalhost + Nginx
Geth30303TCP/UDPP2P networkingPublic
Prysm3500HTTPBeacon APILocalhost + Nginx
Prysm4000RPCgRPC APILocalhost only
Prysm13000TCPP2P networkingPublic
Prysm12000UDPP2P networkingPublic

5. Verification and Testing

a. Check Sync Status

  1. Check Geth sync status: Direct (localhost)

    curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' \
    -H "Content-Type: application/json" http://localhost:8545 | jq

    Through Nginx proxy

    curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' \
    -H "Content-Type: application/json" https://eth-sepolia.yourdomain.com | jq

    Expected output when synced:

    {
        "jsonrpc": "2.0",
        "id": 1,
        "result": false
    }
  2. Check Prysm sync status: Direct (localhost)

    curl -s http://localhost:3500/eth/v1/node/syncing | jq

    Through Nginx proxy

    curl -s https://beacon-sepolia.yourdomain.com/eth/v1/node/syncing | jq

    Expected output when synced:

    {
        "data": {
            "head_slot": "7601769",
            "sync_distance": "0",
            "is_syncing": false,
            "is_optimistic": false,
            "el_offline": false
        }
    }

b. Health Checks

Geth version

curl -s -X POST --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' -H "Content-Type: application/json" https://eth-sepolia.yourdomain.com | jq

Prysm health

curl -s https://beacon-sepolia.yourdomain.com/eth/v1/node/health

6. Monitoring and Maintenance

a. Check Service Status

Docker containers status

docker compose ps

Container resource usage

docker stats geth-sepolia prysm-sepolia

Container logs

docker compose logs --tail=100 geth
docker compose logs --tail=100 prysm

b. Service Management

Restart all services:

docker compose restart

Restart individual services:

docker compose restart geth
docker compose restart prysm

Update services:

docker compose pull
docker compose up -d

c. Data Persistence

All blockchain data is persisted in the ./data directory:

  • ./data/geth-sepolia/ - Geth blockchain data
  • ./data/prysm/ - Prysm beacon chain data
  • ./data/jwt.hex - JWT secret for client authentication

d. Backup Strategy

Create quick backup (while services running):

tar -czf sepolia-backup-$(date +%Y%m%d).tar.gz data/

Create full backup (consistent, services stopped):

docker compose down
tar -czf sepolia-full-backup-$(date +%Y%m%d).tar.gz data/
docker compose up -d

Restore from backup:

docker compose down
tar -xzf sepolia-backup-YYYYMMDD.tar.gz
docker compose up -d

7. Troubleshooting

a. Common Issues

1. Port Conflicts

Check port usage:

netstat -tulpn | grep -E ':(8545|3500|4000|30303|13000|12000)'

Kill processes using required ports:

sudo lsof -ti:8545 | xargs kill -9

2. Insufficient Memory

Check system memory:

free -h

Check Docker memory limits:

docker stats

Increase swap if needed:

sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

3. Sync Issues

Check network connectivity:

ping sepolia.checkpoint-sync.ethpandaops.io

Reset and resync (WARNING: deletes data):

docker compose down
sudo rm -rf data/geth-sepolia data/prysm
docker compose up -d

4. SSL Certificate Issues

Check certificate status:

sudo certbot certificates

Renew certificates manually:

sudo certbot renew

Test Nginx configuration:

sudo nginx -t

5. Access Denied (403)

Check your IP:

curl https://ipinfo.io/ip

Update Nginx IP whitelist and reload:

sudo nano /etc/nginx/sites-available/sepolia-eth-rpc
sudo systemctl reload nginx

b. Log Analysis

Search for errors in container logs

docker compose logs geth | grep -i error
docker compose logs prysm | grep -i error

Check Nginx logs

sudo tail -f /var/log/nginx/sepolia_eth_rpc_access.log
sudo tail -f /var/log/nginx/sepolia_eth_rpc_error.log

Check system logs

sudo journalctl -f -u docker
sudo journalctl -f -u nginx

c. Recovery Commands

Full service restart

docker compose down
docker compose up -d

Full service restart with image updates

docker compose down
docker compose pull
docker compose up -d --force-recreate

Emergency cleanup (removes all data)

docker compose down -v
sudo rm -rf data/
mkdir -p data
openssl rand -hex 32 > data/jwt.hex
docker compose up -d

8. Security Considerations

a. Network Security

  • Local Binding: RPC endpoints bound to localhost only
  • Reverse Proxy: Nginx provides controlled external access
  • IP Whitelisting: Only specified IPs can access endpoints
  • SSL/TLS: All external traffic encrypted
  • Rate Limiting: Protection against abuse

b. Firewall Configuration

Allow SSH

sudo ufw allow ssh

Allow Nginx (HTTP/HTTPS)

sudo ufw allow 'Nginx Full'

Allow P2P ports for blockchain

sudo ufw allow 30303
sudo ufw allow 13000
sudo ufw allow 12000/udp

Deny all other traffic

sudo ufw --force enable

c. Access Control Best Practices

  1. Keep Docker images and system updated
  2. Use SSH keys, disable password auth
  3. Review Nginx logs regularly
  4. Encrypt backups, store securely
  5. Consider VPN for additional security

d. Monitoring Setup

Set up log rotation

sudo nano /etc/logrotate.d/sepolia-logs

Add content:

/var/log/nginx/sepolia_*_rpc_*.log {
    daily
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 644 www-data www-data
    postrotate
        systemctl reload nginx
    endscript
}

9. Sync Time Expectations

  • Initial Sync: 4-8 hours depending on hardware and network
  • Geth Snap Sync: Usually faster than full sync
  • Prysm Checkpoint Sync: Starts from recent checkpoint, significantly faster
  • Storage Growth: ~50-100GB per month for Sepolia

10. Post-Setup Checklist

Verify the following before considering your setup complete:

  • Both Geth and Prysm are syncing successfully
  • RPC endpoints respond to health checks
  • SSL certificates are installed and working
  • IP whitelist configured correctly
  • Firewall rules are properly configured
  • Log rotation is set up
  • Backup strategy is implemented
  • Monitoring is in place

11. Resources

Command Palette
Search for a command to run...