Skip to main content

Run with Docker Compose

Operating system
Network
Execution client
Engine API connection
What you want to run
Initial sync

Before you begin​

  • Linux: Docker Engine with the Compose plugin. See Docker's guides to install Docker Engine and install the Compose plugin. Docker Engine starts at boot, and the node starts with it.
  • macOS: Docker Desktop for Mac. Docker Desktop starts when you sign in, not at boot, and the node only runs while the Mac is awake. In Docker Desktop’s settings, turn on Start Docker Desktop when you sign in to your computer, and under Resources, set the memory to the amount listed in System requirements.

Run the commands on this page in a terminal. This must print v2 or later:

docker compose version

Set up the services​

  1. Create a folder for the node and go into it. The compose file goes here, and the services keep everything they store in their data folder, one folder per network, as in the Quickstart. Docker creates them on first start:

    mkdir -p ~/ethereum-docker && cd ~/ethereum-docker
  2. Save the compose file as ~/ethereum-docker/docker-compose.yml:

    docker-compose.yml
    services:
    jwt-init:
    container_name: jwt-init
    image: alpine:3.22
    volumes:
    - ./data/jwt:/jwt
    command:
    - sh
    - -c
    - test -f /jwt/jwt.hex || od -An -tx1 -N32 /dev/urandom | tr -d ' \n' > /jwt/jwt.hex

    besu:
    container_name: besu
    image: hyperledger/besu:26.9.0
    restart: unless-stopped
    stop_grace_period: 10m
    depends_on:
    jwt-init:
    condition: service_completed_successfully
    command:
    - --network=mainnet
    - --data-path=/data
    - --engine-jwt-secret=/jwt/jwt.hex
    - --engine-host-allowlist=*
    - --rpc-http-host=0.0.0.0
    volumes:
    - ./data/mainnet/besu:/data
    - ./data/jwt:/jwt:ro
    ports:
    - "30303:30303/tcp" # P2P
    - "30303:30303/udp" # P2P discovery

    beacon:
    container_name: beacon
    image: gcr.io/offchainlabs/prysm/beacon-chain:v7.3.1
    restart: unless-stopped
    stop_grace_period: 2m
    depends_on:
    jwt-init:
    condition: service_completed_successfully
    besu:
    condition: service_started
    command:
    - --mainnet
    - --datadir=/data
    - --execution-endpoint=http://besu:8551
    - --jwt-secret=/jwt/jwt.hex
    - --checkpoint-sync-url=https://sync-mainnet.beaconcha.in
    - --beacon-db-pruning
    - --http-host=0.0.0.0
    - --accept-terms-of-use
    volumes:
    - ./data/mainnet/beacon:/data
    - ./data/jwt:/jwt:ro
    ports:
    - "13000:13000/tcp" # libp2p
    - "13000:13000/udp" # libp2p (QUIC)
    - "12000:12000/udp" # discv5
    - "127.0.0.1:3500:3500" # beacon REST API, this machine only

    On every start, jwt-init creates the JWT secret in data/jwt if it isn't there yet, then exits. The services that use the secret wait for it. The Engine API (8551) stays inside the Docker network.

    Only the peer-to-peer ports are published to the internet; ports are published to the internet; the beacon API is published only to 127.0.0.1.

    https://sync-mainnet.beaconcha.in is one of several community checkpoint sync endpoints; any one you trust works.

    --beacon-db-pruning deletes blocks and states older than about 5 months, the oldest data other nodes can still ask for, so the database stops growing. Leave it out if you need the full history.

    Prysm is pinned to v7.3.1, the current release. To update later, change the version in the image: lines and run docker compose up -d again: it downloads the new images and recreates only the containers that changed.

  3. Start the services:

    docker compose up -d

    restart: unless-stopped restarts a container after a crash or reboot, unless you stopped it yourself.

    List the services:

    docker compose ps

    All services should be running.

    Check the beacon node's sync status:

    curl -s http://127.0.0.1:3500/eth/v1/node/syncing

    The node is fully running once the response shows all three of these as false:

    {
    "data": {
    "head_slot": "…",
    "sync_distance": "0",
    "is_syncing": false,
    "is_optimistic": false,
    "el_offline": false
    }
    }
    • "el_offline": false: the beacon node reaches the execution client. This should be the case as soon as both services are up. If it stays true, read the execution client's logs, for example with docker compose logs --tail 50 geth for Geth.

    • "is_syncing": false: the beacon node is synced.

      From a checkpoint, this takes a few minutes: the node starts from a recent finalized state instead of replaying the chain.

    • "is_optimistic": false: the execution client is synced. This takes the longest, from a few hours to a few days depending on your execution client and the network. Until then, the beacon node follows the chain but can't check the blocks’ transactions.

Logs.

docker compose logs -f beacon # follow the beacon node
docker compose logs --since 1h # every service, the last hour

Everyday commands.

docker compose stop # stop everything
docker compose up -d # start it again
docker compose restart beacon # restart one service
docker compose down # remove the containers

docker compose down keeps your data in ~/ethereum-docker/data.

Prysm also keeps the most recent debug logs in <datadir>/logs/: the current file plus one older one, at most about 500 MB. To turn that off, add --disable-ephemeral-log-file.

Check that it works​

Restart the machine once, then run the checks from Start the services again: everything should come back by itself. On macOS and Windows, sign in first so Docker Desktop starts.

Troubleshooting​

A container restarts in a loop. Read the last error:

docker compose logs --tail 50 beacon

A misspelled or removed flag stops Prysm at startup; compare with the beacon-chain reference.