Skip to main content

launch-devnet

Launch erigon (EL) + a CL client on any ethpandaops devnet. Takes a devnet landing-page URL (e.g. https://bal-devnet-3.ethpandaops.io) and discovers everything else (chain id, fork schedule, EL/CL bootnodes, client images, ports) from the network's config service. Generates start/stop/clean scripts, monitors EL/CL progress, and investigates failures by comparing against the rest of the network rather than assuming the bug is in erigon.

Source facts

Repository
erigontech/erigon
Last source activity
May 22, 2026 at 13:00
Detected SKILL.md language
English
Stars
3,608
Forks
1,571

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
launch-devnet
description
Launch erigon (EL) + a CL client on any ethpandaops devnet. Takes a devnet landing-page URL (e.g. https://bal-devnet-3.ethpandaops.io) and discovers everything else (chain id, fork schedule, EL/CL bootnodes, client images, ports) from the network's config service. Generates start/stop/clean scripts, monitors EL/CL progress, and investigates failures by comparing against the rest of the network rather than assuming the bug is in erigon.
allowed-tools
Bash, Read, Write, Edit, Glob, WebFetch
allowed-prompts
[{"tool":"Bash","prompt":"start, stop, and manage erigon and CL client processes for an ethpandaops devnet"}]
# Launch a generic ethpandaops devnet Run erigon (EL) + a CL client on an arbitrary ethpandaops devnet. The user supplies only the landing-page URL; the skill discovers every parameter at runtime so the same flow works for `bal-devnet-N`, `fusaka-devnet-N`, `gloas-devnet-N`, `pectra-devnet-N`, etc. ## Inputs The user provides: 1. **Devnet URL** — landing page like `https://bal-devnet-3.ethpandaops.io`. Required. 2. **Working directory** — defaults to `~/<devnet-name>/`. In auto mode, use the default. Optional overrides (ask only if relevant or ambiguous): - CL client choice (default `lighthouse`). - CL image tag override (default = the tag the network publishes in its inventory). - Port offset (default `+100`; bumped automatically if the offset ports are already bound). - Extra erigon flags / env vars (e.g. `--prune.mode=...`, `ERIGON_EXEC3_PARALLEL=true`). ## Step 1 — Resolve devnet config Derive `DEVNET` from the URL hostname (strip `.ethpandaops.io`). For `https://bal-devnet-3.ethpandaops.io` that is `bal-devnet-3`. Build: - Landing page: `https://${DEVNET}.ethpandaops.io` - Config base: `https://config.${DEVNET}.ethpandaops.io` - Public RPC: `https://rpc.${DEVNET}.ethpandaops.io` - Public beacon: `https://beacon.${DEVNET}.ethpandaops.io` - Checkpoint sync: `https://checkpoint-sync.${DEVNET}.ethpandaops.io` - Dora explorer: `https://dora.${DEVNET}.ethpandaops.io` - Forkmon: `https://forkmon.${DEVNET}.ethpandaops.io` - Spec notes: linked from the landing page (typically `https://notes.ethereum.org/@ethpandaops/<DEVNET>`) Optionally use `WebFetch` on the landing page to confirm the network exists and to grab the spec-notes link. Then download config artefacts: ```bash mkdir -p "$WORKDIR/testnet-config" CFG="https://config.${DEVNET}.ethpandaops.io" curl -fsSL "${CFG}/el/genesis.json" -o "$WORKDIR/genesis.json" curl -fsSL "${CFG}/cl/config.yaml" -o "$WORKDIR/testnet-config/config.yaml" curl -fsSL "${CFG}/cl/genesis.ssz" -o "$WORKDIR/testnet-config/genesis.ssz" curl -fsSL "${CFG}/api/v1/nodes/inventory" -o "$WORKDIR/inventory.json" echo 0 > "$WORKDIR/testnet-config/deploy_block.txt" echo 0 > "$WORKDIR/testnet-config/deposit_contract_block.txt" ``` If any of those 404, the derived name is wrong or the network uses a non-standard layout — ask the user for the correct config base URL. **Do not silently substitute defaults or stub data.** ## Step 2 — Extract network parameters Parse `genesis.json`: - `config.chainId` → `--networkid` for erigon - All `config.*Time` fields → fork timestamps (record for log readability) Parse `testnet-config/config.yaml`: - `CONFIG_NAME`, `MIN_GENESIS_TIME`, `SECONDS_PER_SLOT`, `SLOTS_PER_EPOCH` - Fork epoch fields (`*_FORK_EPOCH`) for scheduled forks Parse `inventory.json` (`ethereum_pairs.<node>.{consensus,execution}`): - `consensus.image` → available CL client images (lighthouse, lodestar, prysm, teku, nimbus). Pick the user's preferred one (default `lighthouse`). - `execution.image` → available EL clients on the network. Keep this list — it's needed later when investigating failures. - `consensus.enr` (all entries) → CL bootnodes - `execution.enode` (all entries) → EL bootnodes - `consensus.beacon_uri` and `execution.rpc_uri` → per-peer endpoints for cross-checking Write a key/value summary to `$WORKDIR/devnet-info.txt` so other skills can parse fields programmatically (one `key: value` per line, no quoting). Required keys: `devnet`, `chain_id`, `port_offset`, `cl_client`, `cl_image`, `authrpc_port`, `http_port`, `ws_port`, `p2p_port`, `cl_http_port`, `cl_p2p_port`, `bootnode_count_el`, `bootnode_count_cl`, `public_rpc`, `public_beacon`. Add fork timestamps as additional `fork_<name>: <unix-ts>` lines. Print the summary to the user and stop for confirmation if anything looks off (wrong chain id, missing forks, no peers in inventory). ## Step 3 — Working directory layout ``` $WORKDIR/ ├── devnet-info.txt # summary of what was fetched (chain id, forks, ports, ...) ├── genesis.json # EL genesis ├── inventory.json # raw inventory (consumed by start scripts and the failure flow) ├── testnet-config/ # CL testnet dir │ ├── config.yaml │ ├── genesis.ssz │ ├── deploy_block.txt │ └── deposit_contract_block.txt ├── start-erigon.sh # FIRST: creates jwt.hex ├── start-cl.sh # SECOND: connects to erigon over engine API ├── stop.sh # stops both ├── clean.sh # stop + wipe data + re-init ├── erigon-data/ # erigon datadir (jwt.hex lives here) ├── cl-data/ # CL client datadir ├── erigon-console.log └── cl-console.log ``` ## Step 4 — Port assignments Default offset is `+100` on top of erigon defaults (see [erigon-network-ports](../erigon-network-ports/SKILL.md) for the full list and CLI flags). Before generating scripts, check every candidate port. Use `lsof` so the check works on macOS and Linux (`ss` is Linux-only): ```bash # TCP listeners for p in <each chosen TCP port>; do lsof -nP -iTCP:$p -sTCP:LISTEN >/dev/null 2>&1 && echo "TCP $p in use" done # UDP (devp2p discovery, torrent, CL QUIC) for p in <each chosen UDP port>; do lsof -nP -iUDP:$p >/dev/null 2>&1 && echo "UDP $p in use" done ``` If any port is already bound, bump the whole offset by +100 (try `+200`, `+300`, …) and re-check. Record the chosen offset in `devnet-info.txt` as `port_offset: <N>` so wrappers like `bal-devnet-ab-test` can read it. Match the CL ports to the same offset family so the user only has one number to remember. ## Step 5 — Initialize erigon datadir ```bash ./build/bin/erigon init --datadir="$WORKDIR/erigon-data" "$WORKDIR/genesis.json" ``` If `./build/bin/erigon` is missing, invoke `/erigon-build` first. ## Step 6 — Generate `start-erigon.sh` Required flags (substitute values discovered in Step 2): - `--datadir=$WORKDIR/erigon-data` - `--externalcl` - `--networkid=<chainId>` - `--bootnodes="<comma-joined enodes from inventory>"` - `--staticpeers="<same enodes>"` — small devnets benefit from explicit static peers - All offset ports (HTTP, WS, authrpc, P2P, torrent, private.api.addr, pprof, metrics) - `--http`, `--http.api=eth,erigon,engine,debug,net,trace,txpool,web3`, `--http.addr=127.0.0.1` - `--ws` - `--authrpc.addr=127.0.0.1`, `--authrpc.vhosts=*` - `--pprof`, `--metrics` so the node is debuggable under load Optional env vars to set in the script body (only when the user asks for them): - `ERIGON_EXEC3_PARALLEL=true`, `ERIGON_EXEC3_WORKERS=12` - `ERIGON_ASSERT=true`, `LOG_HASH_MISMATCH_REASON=true` Do not bake in fork-specific or experimental flags unless the user requests them. The script should be a thin wrapper, not a config dump. End the script with `exec ./build/bin/erigon …` so the script's process is replaced by erigon. That way the PID captured at start time (Step 8) is the erigon PID and `stop.sh` can target it precisely instead of relying on `pkill -f` regex matching. ## Step 7 — Generate `start-cl.sh` The script runs the chosen CL client via Docker with `--network=host`. The recipe below is **Lighthouse-specific** (the default); for other clients (lodestar, prysm, teku, nimbus) see the mapping note at the end of this step. Before baking the checkpoint-sync URL into the script, probe it — fresh devnets sometimes don't have `checkpoint-sync.<devnet>.ethpandaops.io` provisioned yet. If the probe fails, omit the `--checkpoint-sync-url` flag and lighthouse falls back to genesis sync: ```bash CHECKPOINT_URL="https://checkpoint-sync.${DEVNET}.ethpandaops.io" if curl -fsI --max-time 5 "$CHECKPOINT_URL" >/dev/null 2>&1; then CHECKPOINT_FLAG="--checkpoint-sync-url=$CHECKPOINT_URL" else CHECKPOINT_FLAG="" fi ``` Recipe: ```bash JWT="$WORKDIR/erigon-data/jwt.hex" [ -f "$JWT" ] || { echo "JWT $JWT missing — start erigon first"; exit 1; } CL_IMAGE="<CL image from inventory>" # Skip pull if the image is already cached (idempotent + offline-friendly) docker image inspect "$CL_IMAGE" >/dev/null 2>&1 || docker pull "$CL_IMAGE" docker rm -f "${DEVNET}-cl" 2>/dev/null || true docker run -d --name "${DEVNET}-cl" --network=host \ -v "$WORKDIR/testnet-config":/config:ro \ -v "$WORKDIR/cl-data":/data \ -v "$JWT":/jwt.hex:ro \ "$CL_IMAGE" \ lighthouse bn \ --testnet-dir=/config \ --datadir=/data \ --execution-endpoint=http://127.0.0.1:<authrpc port> \ --execution-jwt=/jwt.hex \ --boot-nodes="<comma-joined ENRs>" \ --port=<CL P2P> --quic-port=<CL QUIC> \ --http --http-port=<CL HTTP> --http-address=127.0.0.1 \ --metrics --metrics-port=<CL metrics> \ $CHECKPOINT_FLAG \ --disable-enr-auto-update \ --suggested-fee-recipient=0x0000000000000000000000000000000000000000 ``` Notes on the recipe: - `--http-address=127.0.0.1` keeps the beacon API local; only widen to `0.0.0.0` if the user explicitly needs remote access. - The `[ -f "$JWT" ] || …` line **aborts** the script if `jwt.hex` is missing. Step 8's polling loop should make this guard a fast-path no-op, but it's the last line of defence against a CL trying to authenticate with a non-existent token. For non-lighthouse CLs, the binary name and flag spellings differ. A quick mapping for the common ones (verify against the client's docs before using): | Client | Subcommand | datadir flag | execution endpoint flag | | ---------- | --------------------------------- | ------------------ | ----------------------- | | lighthouse | `lighthouse bn` | `--datadir` | `--execution-endpoint` | | lodestar | `node /usr/app/.../lodestar beacon` | `--dataDir` | `--execution.urls` | | prysm | `beacon-chain --accept-terms-of-use` | `--datadir` | `--execution-endpoint` | | teku | `teku` | `--data-base-path` | `--ee-endpoint` | | nimbus | `nimbus_beacon_node` | `--data-dir` | `--web3-url` | Don't guess flag names — when unsure, ask the user or check the ethpandaops repo for the exact invocation. ## Step 8 — Start, in order Erigon must start first because it generates `jwt.hex`. Capture its PID for clean shutdown, then **poll** for `jwt.hex` and the authrpc port — do not rely on `sleep`. Only after both conditions are true is it safe to start the CL (otherwise the CL fails JWT auth on the first `newPayload` call). ```bash cd "$WORKDIR" && nohup bash start-erigon.sh > erigon-console.log 2>&1 & echo $! > "$WORKDIR/erigon.pid" AUTHRPC=<authrpc port> for i in $(seq 1 60); do if [ -f "$WORKDIR/erigon-data/jwt.hex" ] \ && lsof -nP -iTCP:"$AUTHRPC" -sTCP:LISTEN >/dev/null 2>&1; then break fi sleep 1 done [ -f "$WORKDIR/erigon-data/jwt.hex" ] || { echo "jwt.hex not produced after 60s — check erigon-console.log"; exit 1; } lsof -nP -iTCP:"$AUTHRPC" -sTCP:LISTEN >/dev/null 2>&1 || { echo "authrpc port $AUTHRPC not bound after 60s"; exit 1; } cd "$WORKDIR" && nohup bash start-cl.sh > cl-console.log 2>&1 & ``` Verify before moving on: - `tail -n 80 erigon-console.log` — no fatal errors, engine API listener up - `lsof -nP -iTCP:<authrpc port> -sTCP:LISTEN` — bound by erigon - `tail -n 80 cl-console.log` — CL connected to engine API and starting checkpoint sync (or genesis sync if checkpoint endpoint is unavailable) ## Step 9 — Monitoring Run these at intervals and report a single status line each pass: ```bash HTTP=<erigon http port>; CL=<cl http port> hex_to_int() { local h; h=$(jq -r '.result'); printf '%d\n' "$h"; } # Erigon head EH=$(curl -s http://localhost:$HTTP -X POST -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ | hex_to_int) # Network head from public RPC
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub