How to run an Ethereum execution & consensus client with Ubuntu & Docker

→ updated on 01/20/23 for post-merge compatability & fixes

This is a quick & dirty guide to running Besu (execution client) & Lighthouse (consensus client) with Ubuntu and Docker primarily for testnet usage. It is aimed to be minimalistic and has client diversity in mind.

As a powerful sandboxing tool Docker offers high flexibility at minimized resource consumption and allows for potentially switching to different clients with ease (see bonus section of this guide).

Besu’s (by ConsenSys) latest additions of a refreshingly slim database structure (“Bonsai”) and checkpoint-sync functionality heave it back in contention with good ‘ol Geth as an execution client of choice. Lighthouse (by Sigma Prime) serves as lightweight, highly-reliable (beacon chain) consensus client, making it a great fit for a combined post-merge node setup.

Quick reminder: post-merge validators need to run both an execution AND consensus client to to be able to publish attestations and block proposals.

This guide assumes a pre-installed and hardened Ubuntu installation as well as Docker. Excellent introductory resources to start with are: eth-docker, coincashew and rocketpool

In order to have good peering for your clients you moreover want to open ports 30303 (Besu) and 9000 (Lighthouse) to the internet (TCP & UDP).

A potentially mainnet-proof hardware setup could look like:

  • Quad Core CPU (e.g. on a dedicated local Mini-PC like a Intel NUC10 or Asus PN50)

  • 16 GB RAM

  • 1-2 TB SSD (ideally NVMe, 2TB recommended for Besu mainnet database growth peace-of-mind)

If you intend to deploy on a VPS, please bear in mind extra adaptions for security.

Setting up an execution client (Besu) with Docker

Navigate in a terminal/CLI to your user’s /home directory.

Create a project directory with subfolders and retrieve the Besu Docker image

$ mkdir -p ETHclient/{besu,lighthouse,teku,JWT}; cd ETHclient
$ docker pull hyperledger/besu:latest

Depending on your Docker configuration every Docker command should be preceded by sudo

Check the image

$ docker run hyperledger/besu:latest besu --version

Generate a JasonWebToken (JWT) so that the execution and consensus client can communicate securely

$ openssl rand -hex 32 | tr -d "\n" > "$(pwd)/JWT/jwtsecret"

Create a Docker network (to which your Docker containers will connect to)

$ docker network create ETHclient_net

Start/Run Besu (creates a container)

note: it will take some time to sync to the Ethereum Goerli testnet which is needed so that the consensus client functions properly.

$ docker run -d -it --rm --name besu --network ETHclient_net -p 30303:30303/tcp -p 30303:30303/udp -p -p -e JAVA_OPTS=-Xmx8g -v $HOME/ETHclient/besu:/opt/besu/data -v $HOME/ETHclient/JWT:/JWT hyperledger/besu:latest --network=goerli --data-path=/opt/besu/data/data --data-storage-format=BONSAI --sync-mode=X_CHECKPOINT --rpc-http-enabled --rpc-http-host= --rpc-http-api=ETH,NET,WEB3 --rpc-http-cors-origins=* --host-allowlist=* --engine-host-allowlist=* --engine-jwt-secret=/JWT/jwtsecret --engine-rpc-port=8551

Setting up a consensus client (Lighthouse) with Docker

Retrieve the Lighthouse Docker image

$ docker pull sigp/lighthouse:latest-modern

Check the image

$ docker run sigp/lighthouse:latest-modern lighthouse --version

Start/Run Lighthouse

note: it is recommended to use the lightning fast checkpoint sync feature, representing a secure way to sync the PoS chain. Verify the initial state against a curated list of endpoint providers.

$ docker run -d -it --rm --name lh_beacon --network ETHclient_net -p 9000:9000/tcp -p 9000:9000/udp -p -v $HOME/ETHclient/lighthouse:/root/.lighthouse -v $HOME/ETHclient/JWT:/JWT sigp/lighthouse:latest-modern lighthouse beacon --network=goerli --execution-endpoints=http://besu:8551 --execution-jwt=/JWT/jwtsecret --http --http-address --checkpoint-sync-url=

Additional remarks & thoughts


Easily log/monitor containers with

$ docker logs -f besu  # (exit with Ctrl+c)
$ docker logs -f lh_beacon


Stop container(s)

$ docker stop besu
$ docker stop lh_beacon

Update Besu / Lighthouse

Stop each container respectively before

$ docker pull hyperledger/besu:latest
$ docker pull sigp/lighthouse:latest-modern

Other potentially helpful Docker commands

$ docker ps   # list containers
$ docker container prune   # prune dangling containers
$ docker inspect network ETHclient_net   # check Docker network

Optional / Bonus section

Switch from testnet (Goerli) to mainnet

remove the second appearance of the --network flag in the above described run commands, respectively (i.e. specifying the application, not the docker network)

Use your own node with MetaMask to broadcast transactions

Follow instructions and add “RPC URL” to circumvent centralized node infrastructure and instead use your own local node for chain interactions.

Welcome to the true rails of web3! 🥳

Switch consensus clients from Lighthouse to Teku

Stop Lighthouse → retrieve Teku Docker image → check the image → start/run Teku:

$ docker stop lh_beacon
$ docker pull consensys/teku:latest
$ docker run consensys/teku:latest teku --version
$ docker run -d -it --rm --name teku --network ETHclient_net -p 9000:9000/tcp -p 9000:9000/udp -p -e JAVA_OPTS=-Xmx4g -v $HOME/ETHclient/teku:/var/lib/teku -v $HOME/ETHclient/JWT:/JWT consensys/teku:latest --data-base-path=/var/lib/teku --network=goerli --p2p-port=9000 --ee-endpoint=http://besu:8551 --ee-jwt-secret-file=/JWT/jwtsecret --rest-api-enabled=true --rest-api-docs-enabled=true 

Become a testnet validator (using Lighthouse)

This guide assumes you’ve worked your way through

and have created your keystore-files. Subsequently, watch here how to deposit to the Prater testnet deposit contract. Afterwards:

  1. Copy your validator keys into your mounted docker volume - following along the above described way, you will have to copy the “validator_keys” directory into $HOME/lh-besu/lighthouse

  2. Import your validator keys into Lighthouse

$ docker run -it --rm -v $HOME/ETHclient/lighthouse:/root/.lighthouse sigp/lighthouse:latest-modern lighthouse account validator import --directory=/root/.lighthouse/validator_keys

note: as the validator account manager account validator import is running from inside Docker, a directory inside the container storage has to be referenced - as opposed to a local drive.

Start/Run Lighthouse validator

Make sure to NEVER run two instances of the same validating keys at once to avoid getting slashed and consequently lose your (testnet)ETH

$ docker run -d -it --rm --name lh_validator --network ETHclient_net -v $HOME/ETHclient/lighthouse:/root/.lighthouse sigp/lighthouse:latest-modern lighthouse vc --network=goerli --beacon-nodes="http://lh_beacon:5052"

Monitor your validator

and identify your validating public key(s) starting with “0x”

$ docker logs -f lh_validator

alternatively you can check $HOME/ETHclient/lighthouse/prater/validators/validator_definitions.yml

Also, monitor the status of your validator(s) by entering your validating public key(s) on

Voluntarily exit a validator

$ docker exec -it lh_validator lighthouse account validator exit --beacon-node http://lh_beacon:5052 --keystore /root/.lighthouse/prater/validators/<0x_yourvalidatorkey>/voting-keystore.json

EthStaker (shout-out to Yorick for invaluable feedback!), ConsenSys, and Lighthouse Discord servers are very welcoming places for further questions and advice.


Please keep in mind this is a testnet guide that may contain mistakes and that takes shortcuts which come with trade-offs. It could quickly become outdated as it’s subject to ever evolving network and client changes.


featured image CC BY-NC 2.0 by Barry Stock


This post was retroactively supported by a grant from a CLR funding round held by EthStaker, mainly matched by the EF.

Subscribe to Ladislaus
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
This entry has been permanently stored onchain and signed by its creator.