back to docs
mainnet

Geth + Prysm

Deploy an Ethereum Mainnet RPC node with Geth + Prysm

This guide walks you through setting up your own Ethereum Mainnet 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 Ethereum Mainnet (ETH & Beacon).

Deployment Method
This guide uses Docker to run all services as containers. Make sure Docker is installed before proceeding.
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
Storage2 TB NVMe SSD4 TB NVMe SSD
Network25 Mbps stable100+ Mbps
Note
Ethereum mainnet requires more storage than testnets. Storage grows ~50-80GB/month. Plan accordingly.
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 ethereum-rpc && cd ethereum-rpc

Create required directories:

mkdir -p data/geth-mainnet data/prysm-beacon

Generate JWT secret:

openssl rand -hex 32 > data/jwt.hex

Create Docker Compose file:

nano docker-compose.yml

Add Docker Compose configuration:

services:
  geth:
    image: ethereum/client-go:v1.16.7
    container_name: geth-mainnet
    restart: unless-stopped

    ports:
      - "127.0.0.1:8545:8545"     # HTTP RPC
      - "127.0.0.1:8546:8546"     # WS RPC
      - "30303:30303"             # P2P TCP
      - "30303:30303/udp"         # P2P UDP

    volumes:
      - ./data/geth-mainnet:/root/.ethereum
      - ./data/jwt.hex:/root/jwt.hex:ro

    command: >
      --mainnet
      --syncmode snap
      --cache 32768

      --http
      --http.addr 0.0.0.0
      --http.port 8545
      --http.api eth,net,web3
      --http.vhosts "*"
      --http.corsdomain "*"

      --ws
      --ws.addr 0.0.0.0
      --ws.port 8546
      --ws.api eth,net,web3
      --ws.origins "*"

      --authrpc.addr 0.0.0.0
      --authrpc.port 8551
      --authrpc.jwtsecret /root/jwt.hex
      --authrpc.vhosts=*

      --maxpeers 100
      --verbosity 3

  prysm:
    image: gcr.io/prysmaticlabs/prysm/beacon-chain:stable
    container_name: prysm-beacon
    restart: unless-stopped

    depends_on:
      - geth

    ports:
      - "127.0.0.1:3500:3500"     # HTTP API (beacon)
      - "4000:4000"               # P2P

    volumes:
      - ./data/prysm-beacon:/data
      - ./data/jwt.hex:/root/jwt.hex:ro

    command: >
      --datadir=/data
      --accept-terms-of-use
      --execution-endpoint=http://geth-mainnet:8551
      --jwt-secret=/root/jwt.hex
      --p2p-max-peers=100
      --p2p-host-ip=0.0.0.0
      --monitoring-host=127.0.0.1
      --checkpoint-sync-url=https://beaconstate-mainnet.chainsafe.io

networks:
  default:
    name: eth-mainnet-network
    driver: bridge

c. Initialize and start services

Start the services:

docker compose up -d

View Geth logs:

docker logs -f geth-mainnet

View Prysm logs:

docker logs -f prysm-beacon

Monitor both services:

docker compose logs -f

3. Nginx reverse proxy setup

a. Configure Nginx for Geth (execution layer)

Create Nginx configuration:

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

Add the following configuration:

server {
    server_name eth.yourdomain.com;

    access_log /var/log/nginx/eth_mainnet_rpc_access.log;
    error_log /var/log/nginx/eth_mainnet_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_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;

        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

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

b. Configure Nginx for Prysm (consensus layer)

Create Nginx configuration:

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

Add the following configuration:

server {
    server_name beacon.yourdomain.com;

    access_log /var/log/nginx/beacon_mainnet_rpc_access.log;
    error_log /var/log/nginx/beacon_mainnet_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_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;

        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/eth-mainnet-rpc /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/beacon-mainnet-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.yourdomain.com

Obtain SSL certificate for Prysm endpoint:

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

Test certificate renewal:

sudo certbot renew --dry-run

4. Service configuration details

a. Geth (execution client)

  • Image: ethereum/client-go:v1.16.7
  • Network: Ethereum mainnet
  • Sync Mode: Snap sync
  • RPC Endpoint: https://eth.yourdomain.com
  • WebSocket Endpoint: wss://eth.yourdomain.com
  • Available APIs: eth, net, web3
  • Cache: 32768 MB

b. Prysm (consensus client)

  • Image: gcr.io/prysmaticlabs/prysm/beacon-chain:stable
  • Network: Ethereum mainnet
  • HTTP API: https://beacon.yourdomain.com
  • Checkpoint Sync: Enabled for faster initial sync

c. Port configuration

ServicePortProtocolPurposeAccess
Geth8545HTTPJSON-RPC APILocalhost + Nginx
Geth8546WSWebSocket RPCLocalhost + Nginx
Geth30303TCP/UDPP2P networkingPublic
Prysm3500HTTPBeacon APILocalhost + Nginx
Prysm4000TCPP2P 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.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.yourdomain.com/eth/v1/node/syncing | jq

    Expected output when synced:

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

b. Health checks

Geth block number

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

Prysm health

curl -s https://beacon.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-mainnet prysm-beacon

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-mainnet/: Geth blockchain data
  • ./data/prysm-beacon/: Prysm beacon chain data
  • ./data/jwt.hex: JWT secret for client authentication

d. Backup strategy

Create quick backup (while services running):

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

Create full backup (consistent, services stopped):

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

Restore from backup:

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

7. Troubleshooting

a. Common issues

1. Port conflicts

Check port usage:

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

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 beaconstate-mainnet.chainsafe.io

Reset and resync (WARNING: deletes data):

docker compose down
sudo rm -rf data/geth-mainnet data/prysm-beacon
mkdir -p data/geth-mainnet data/prysm-beacon
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/eth-mainnet-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/eth_mainnet_rpc_access.log
sudo tail -f /var/log/nginx/eth_mainnet_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/geth-mainnet data/prysm-beacon
mkdir -p data/geth-mainnet data/prysm-beacon
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 4000

Deny all other traffic

sudo ufw --force enable

c. Access control best practices

  1. Regular Updates: Keep Docker images and system updated
  2. Strong Authentication: Use SSH keys, disable password auth
  3. Monitor Access: Review Nginx logs regularly
  4. Backup Security: Encrypt backups, store securely
  5. Network Segmentation: Consider VPN for additional security

d. Monitoring setup

Set up log rotation

sudo nano /etc/logrotate.d/eth-mainnet-logs

Add content:

/var/log/nginx/eth_mainnet_rpc_*.log
/var/log/nginx/beacon_mainnet_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: 6-12 hours depending on hardware and network
  • Geth Snap Sync: Faster than full sync
  • Prysm Checkpoint Sync: Starts from recent checkpoint, much faster
  • Storage Growth: ~50-80GB per month for mainnet

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...