Introduction
Ethereum Devnets are fully customisable private networks designed for development purposes, allowing complete control over configurations, validators, and network parameters. This guide walks through the process of setting up an Ethereum L1 Devnet using Reth as the execution client and Lighthouse as the consensus client.
Why do we need it
Having a safe, customisable environment for testing is essential. That’s where a devnet comes in. A development network is a private Ethereum network that gives developers full control over how it runs. It’s an ideal playground for building, experimenting, and debugging; without the risks or costs of deploying on a live chain.
We created our devnet using Reth and Lighthouse to serve as a reliable and flexible testing ground for L1 infrastructure. With this setup, we can test new features, simulate validator behavior, and tweak chain parameters in a completely isolated environment. This allows us to catch issues early, optimize configurations, and ensure our changes won’t introduce instability when rolled out to production.
One of the main problems the devnet solves is visibility and control. Public testnets are often shared by many users and can be unpredictable or unavailable. With our own devnet, we have a consistent and fully observable environment where we can monitor performance metrics, fine-tune validator behavior, and rapidly iterate on upgrades or custom logic.
Our devnet acts as a powerful internal tool for research and development. Whether we’re onboarding new validators, testing smart contract deployments, or experimenting with protocol-level changes, it gives us the confidence to move fast without breaking things.
Setting Up the L1 Node
Step 1: Install Dependencies
- curl
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
- build-essential
- foundry (optional if you want to use cool tool like
cast
)
Step 2: Generate the Genesis File
Copy the value file to the current directory for future use.
And then replace the GENESIS_TIMESTAMP
with the future time because you need to get the node running before the genesis time. the following is an ansible instruction
- name: Get future timestamp
shell: date +%s -d "+10 minutes"
register: genesis_timestamp
- name: Update GENESIS_TIMESTAMP in values.env
lineinfile:
path: /home/ubuntu/config/values.env
regexp: '^export GENESIS_TIMESTAMP='
line: "export GENESIS_TIMESTAMP=\"{{ genesis_timestamp.stdout }}\""
create: yes
Then use the ethereum-genesis-generator from ethpandaops to generate the genesis file.
- name: Generate genesis
command: >
docker run --rm -it \
-u 0 \
-v /home/ubuntu/output:/data \
-v /home/ubuntu/config/values.env:/config/values.env \
ethpandaops/ethereum-genesis-generator:latest all
Step 3: Create a Validator keystores In order to get the network working we need a validator as well. Use the following to generate the validator keystores
- name: Create validator keystores
command: >
docker run --rm -t --entrypoint eth2-val-tools \
--platform linux/amd64 \
-v /home/ubuntu/output/validator_keys:/output \
ethpandaops/ethereum-genesis-generator:{{ genesis_generator_version }} \
keystores --insecure \
--out-loc="/output/lighthouse-reth-1" \
--source-min="0" \
--source-max="200" \
--source-mnemonic=$SOURCE-MNEMONIC
Step 4: Generate the JWT for communication between EC and BC
- name: Generate JWT secret
shell: |
openssl rand -hex 32 | tr -d "\n" > jwt.hex
args:
executable: /bin/bash
chdir: /home/ubuntu/network-config
Step 5: Configure Docker Compose
docker compose -f docker-compose.yml up -d
# docker-compose.yml
services:
reth:
image: ghcr.io/paradigmxyz/reth:latest
container_name: reth
command: >
node
--chain /config/genesis.json
--datadir /root/.reth
--http
--http.addr 0.0.0.0
--http.api all
--ws
--ws.api all
--disable-discovery
--authrpc.jwtsecret /secrets/jwt.hex
--authrpc.addr 0.0.0.0
--authrpc.port 8551
--metrics 0.0.0.0:9003
volumes:
- ./output/metadata/genesis.json:/config/genesis.json
- ./network-config/jwt.hex:/secrets/jwt.hex
- reth_data:/root/.reth
ports:
- "8545:8545" # HTTP-RPC API
- "30303:30303" # P2P
- "8551:8551" # Engine
- "9003:9003" # Metrics
lighthouse:
image: sigp/lighthouse:latest
container_name: lighthouse
command: >
lighthouse beacon_node
--datadir /root/.lighthouse
--listen-address 0.0.0.0
--port 9000
--http
--http-address 0.0.0.0
--http-port 5052
--slots-per-restore-point 8192
--disable-packet-filter
--execution-endpoint http://reth:8551
--execution-jwt /secrets/jwt.hex
--testnet-dir /config
--metrics
--metrics-address 0.0.0.0
--metrics-allow-origin=*
--metrics-port 5054
--enable-private-discovery
--debug-level debug
--allow-insecure-genesis-sync
volumes:
- ./output/metadata:/config
- ./output/metadata/genesis.ssz:/config/genesis.ssz
- ./network-config/jwt.hex:/secrets/jwt.hex
- lighthouse_data:/root/.lighthouse
depends_on:
- reth
ports:
- "9000:9000" # P2P
- "5052:5052" # HTTP API
- "5054:5054" # Metrics
lighthouse_validator:
image: sigp/lighthouse:latest
container_name: lighthouse_validator
command: >
lighthouse validator_client
--testnet-dir /config
--validators-dir /root/.lighthouse/validator_keys/keys
--secrets-dir /root/.lighthouse/validator_keys/secrets
--suggested-fee-recipient=$suggested-fee-recipient
--init-slashing-protection
--beacon-nodes http://lighthouse:5052
--metrics
--metrics-address=0.0.0.0
--metrics-allow-origin=*
--metrics-port 5064
--graffiti $graffiti
volumes:
- ./output/metadata:/config
- ./output/validator_keys/lighthouse-geth-1/keys:/root/.lighthouse/validator_keys/keys
- ./output/validator_keys/lighthouse-geth-1/secrets:/root/.lighthouse/validator_keys/secrets
- ./validators:/root/.lighthouse/validators
- lighthouse_data:/root/.lighthouse
depends_on:
- lighthouse
ports:
- "5062:5062" # HTTP API
- "5064:5064" # Metrics
volumes:
reth_data:
lighthouse_data:
Step 6: Test the Network
Use cast to send transactions and verify that the network is producing blocks
cast send 0xAF48eDe4D7a1692fFc1e16033C94762fBdE6823B \
--value 1ether \
--rpc-url http://localhost:8545 \
--mnemonic "$(cat ./mnemonic.txt)" \
--mnemonic-derivation-path "m/44'/60'/0'/0/1"
The transaction will go through if the devnet is set up properly.
Wrapping it up
You've now got your own fully customised Ethereum L1 Devnet running with Reth and Lighthouse. Whether you're testing new features, tweaking validator settings, or experimenting with chain configurations, this setup gives you full control over your network.
But we didn’t stop there. We also set up real-time monitoring and dashboards to keep track of network performance, making debugging and performance tuning much easier. If you’re interested in building your own Devnet, need help with monitoring, or just want to help over blockchain infra, feel free to contact us!