Jesse A. Green / notebook

base/node User Guide

Generated April 2026 from base/node at d4a32b2

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Run a full node on the Base Network — Coinbase's Ethereum Layer 2 built on the OP Stack. The base/node repository ships Docker builds, env templates, and entrypoint scripts that compose an execution client (Reth, Geth, or Nethermind) with a consensus client (op-node or base-consensus) into a reproducible Base node deployment.

This guide walks you through getting a Base node up and syncing — from the lowest-friction docker compose up --build path through configuration, day-to-day operations, troubleshooting, and FAQ.


1. Quick Start

Get a Base mainnet node syncing in four steps.

1.1 What You Need

  • Docker Engine 20.10+ with the Compose v2 plugin (docker compose, two words)
  • An Ethereum L1 RPC endpoint (your own node, Alchemy, Infura, QuickNode, etc.)
  • An Ethereum L1 beacon endpoint and a beacon archiver (CL node serving historical blob sidecars)
  • ~32 GB RAM, NVMe SSD with room for 2 × current chain size + snapshot + 20 % buffer

1.2 Lowest-Friction Path

# 1. Clone the repo
git clone https://github.com/base/node.git
cd node

# 2. Edit .env.mainnet — set your L1 endpoints:
#      OP_NODE_L1_ETH_RPC=<your-l1-rpc>
#      OP_NODE_L1_BEACON=<your-l1-beacon>
#      OP_NODE_L1_BEACON_ARCHIVER=<your-l1-beacon-archiver>

# 3. (Optional) Pick a client — reth, geth, or nethermind.
#    The shipped .env defaults to USE_BASE_CONSENSUS=true which only works with reth.
export CLIENT=reth

# 4. Start the stack
docker compose up --build
[+] Building 0.0s (0/0)
[+] Running 2/2
 Container node-execution-1  Created
 Container node-node-1       Created
Attaching to execution-1, node-1
execution-1  | Starting reth with additional args:
node-1       | Using Base Client
...

That's it. Two containers come up: execution (the chosen client) and node (the consensus client). Chain data lands in ./reth-data/ (or ./geth-data/, ./nethermind-data/) on the host.

Tip

A first-time sync from genesis takes days. To get to chain head in hours, drop a published snapshot into HOST_DATA_DIR before starting — see Section 5.5 (Snapshot/Restore) and the official snapshots index at basechaindata.vercel.app.

Important

Without L1 endpoints filled into the env file, the consensus client will fail to start syncing. Set OP_NODE_L1_ETH_RPC, OP_NODE_L1_BEACON, and OP_NODE_L1_BEACON_ARCHIVER (and the BASE_NODE_* equivalents if you use USE_BASE_CONSENSUS=true) before launching.

1.3 Verify It Is Running

curl -s -X POST http://localhost:8545 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
{"jsonrpc":"2.0","id":1,"result":"0x1a3b4c5"}

Anything other than "0x0" means the execution client is alive and at a real block number. See Section 4 for full verification including peer count and consensus sync.

1.4 What To Try Next

  • Section 2 — Install all prerequisites (Docker, dependencies)
  • Section 3 — Configure for mainnet vs Sepolia testnet, ports, logs, metrics
  • Section 4 — Day-to-day commands (logs, restart, health checks)
  • Section 5 — Recipe-driven tasks (switch network, restore snapshot, upgrade)
  • Section 6 — Troubleshooting common errors

2. Installation

2.1 System Requirements

Resource Minimum Recommended
CPU Modern multicore 8+ cores
RAM 32 GB 64 GB
Disk NVMe SSD NVMe SSD (RAID 0 across local drives)
Storage size 2 × current chain size + snapshot size + 20 % same
Filesystem ext4 ext4
Docker Engine 20.10+ with Compose v2 plugin same
Network Public IP, ports 30303 + 9222 (TCP+UDP) reachable same

Live chain size is tracked at base.org/stats; snapshot sizes appear at basechaindata.vercel.app.

Note

Coinbase's production runs use AWS i7i.12xlarge instances with all local NVMe drives in RAID 0 on ext4, for both Reth archive and Geth full nodes.

2.2 Install Docker

Linux (Ubuntu/Debian)

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# log out and back in so your user picks up the docker group
docker --version
docker compose version

macOS

brew install --cask docker
open -a Docker
docker --version
docker compose version

Windows

Install Docker Desktop. After installation, start Docker Desktop and verify in PowerShell:

docker --version
docker compose version

Warning

The legacy docker-compose (v1, hyphenated) command is deprecated. Use docker compose (two words) — issue #1012 tracked the same fix in this repo's reth/README.md.

2.3 Clone The Repository

git clone https://github.com/base/node.git
cd node

The repository ships these top-level components:

Path Purpose
docker-compose.yml Two-container orchestration: execution + node
.env Host-side defaults — CLIENT, HOST_DATA_DIR, USE_BASE_CONSENSUS
.env.mainnet Mainnet env file (consumed by both containers via env_file:)
.env.sepolia Sepolia testnet env file
versions.env / versions.json Pinned upstream commit/tag for each binary
geth/, nethermind/, reth/ Per-client Dockerfile and entrypoint script
consensus-entrypoint Dispatcher: chooses op-node vs. base-consensus
op-node-entrypoint, base-consensus-entrypoint Consensus-client launchers
supervisord.conf Process supervisor used inside containers
dependency_updater/ Go CI utility that bumps the pinned upstream versions
SECURITY.md HackerOne reporting (Coinbase bug bounty program)

2.4 Choose A Client

Base supports three execution clients and two consensus implementations.

2.4.1 Execution Clients

Client CLIENT= Language Built from Final image base
Reth reth Rust base/base (pinned in versions.json) ubuntu:24.04
Geth geth Go ethereum-optimism/op-geth ubuntu:24.04
Nethermind nethermind .NET NethermindEth/nethermind mcr.microsoft.com/dotnet/aspnet:10.0-noble

Pick by exporting CLIENT before docker compose up:

CLIENT=reth docker compose up --build
CLIENT=geth docker compose up --build
CLIENT=nethermind docker compose up --build

Note

If you don't export CLIENT, the compose file falls back to geth (${CLIENT:-geth}). The README mentions reth as the recommended client, but the compose default is geth — track issue #1010. Be explicit about CLIENT in production.

2.4.2 Consensus Clients

The node service runs one of two consensus binaries, picked by USE_BASE_CONSENSUS:

Setting Script invoked Binary When to use
USE_BASE_CONSENSUS=true base-consensus-entrypoint base-consensus (Rust, from base/base) Reth only. Default in shipped .env.
USE_BASE_CONSENSUS=false (or unset) op-node-entrypoint op-node (Go, from ethereum-optimism/optimism) Any client; standard OP Stack consensus.

The dispatcher (consensus-entrypoint) selects the entrypoint:

if [ "${USE_BASE_CONSENSUS:-false}" = "true" ]; then
    if [ -f ./base-consensus-entrypoint ]; then
        echo "Using Base Client"
        exec ./base-consensus-entrypoint
    else
        echo "Base client is not supported for this node type"
        exit 1
    fi
else
    echo "Using OP Node"
    exec ./op-node-entrypoint
fi

Warning

base-consensus is shipped only in the reth image. Setting USE_BASE_CONSENSUS=true with CLIENT=geth or CLIENT=nethermind causes the consensus container to exit with Base client is not supported for this node type. Pair USE_BASE_CONSENSUS=true with CLIENT=reth, or set USE_BASE_CONSENSUS=false for geth/nethermind. See issue #1016.

2.5 Post-Install Verification

docker --version
docker compose version
ls .env.mainnet .env.sepolia versions.env versions.json
Docker version 27.x.x, build ...
Docker Compose version v2.x.x
.env.mainnet
.env.sepolia
versions.env
versions.json

If any of those files are missing, re-clone the repository — these are the source of truth for chain config and pinned dependencies.


3. Configuration

All runtime configuration is in two env files:

  1. .env — host-side defaults consumed by docker compose itself. Controls CLIENT, HOST_DATA_DIR, and USE_BASE_CONSENSUS.
  2. .env.mainnet or .env.sepolia — loaded into both containers via env_file:. Holds the network-specific and client-tuning variables.

3.1 Pick Your Network

# Mainnet (default)
docker compose up --build

# Sepolia testnet
NETWORK_ENV=.env.sepolia docker compose up --build

The compose file uses ${NETWORK_ENV:-.env.mainnet} for the env_file selection.

3.2 Required L1 Endpoints

Edit the env file you are using and replace the placeholder values:

# In .env.mainnet (or .env.sepolia)
OP_NODE_L1_ETH_RPC=https://your-l1-execution-rpc
OP_NODE_L1_BEACON=https://your-l1-beacon
OP_NODE_L1_BEACON_ARCHIVER=https://your-l1-beacon-archiver

If you run the Base-consensus variant (reth + USE_BASE_CONSENSUS=true), also set:

BASE_NODE_L1_ETH_RPC=https://your-l1-execution-rpc
BASE_NODE_L1_BEACON=https://your-l1-beacon

3.3 L1 Provider Hint

OP_NODE_L1_RPC_KIND tells op-node how to fetch receipts efficiently:

Value Provider
alchemy Alchemy
quicknode QuickNode
infura Infura
parity OpenEthereum / Parity
nethermind Nethermind
debug_geth Geth with debug namespace (default in shipped env files)
erigon Erigon
basic Standard eth_getBlockReceipts only
any Try any supported optimized method
standard Standard methods including newer optimized ones

Set it to match your L1 provider:

OP_NODE_L1_RPC_KIND="alchemy"

3.4 Data Directory

HOST_DATA_DIR in .env controls where chain data is persisted on the host (bind-mounted into /data inside the execution container):

HOST_DATA_DIR=./geth-data        # default when CLIENT=geth
HOST_DATA_DIR=./reth-data        # override when CLIENT=reth
HOST_DATA_DIR=./nethermind-data

These names match .dockerignore entries so they never bloat Docker build context.

3.5 Ports Exposed

The compose file publishes these ports from each container:

Container Container port Host port Purpose
execution 8545 8545 HTTP JSON-RPC
execution 8546 8546 WebSocket JSON-RPC
execution 6060 7301 Execution metrics (Prometheus)
execution 30303 30303 (TCP+UDP) Execution P2P
node 8545 7545 Consensus JSON-RPC
node 9222 9222 (TCP+UDP) Consensus P2P
node 7300 7300 Consensus metrics
node 6060 6060 Consensus pprof

Important

Ports 30303/tcp+udp and 9222/tcp+udp must be reachable from the public internet for peer discovery to work. Open them in your router/firewall. The entrypoints auto-detect your public IP via ifconfig.me, api.ipify.org, ipecho.net, or v4.ident.me and advertise it to peers.

Caution

All published RPC and metrics ports bind to 0.0.0.0 inside the container. If your host has a public IP without a firewall, your RPC is open to the internet. Restrict with iptables/ufw or rebind in docker-compose.yml (see the Admin Guide §7).

3.6 Geth Cache Tuning

Defaults in the shipped env files target a 64 GB+ host:

GETH_CACHE="20480"           # 20 GB total cache
GETH_CACHE_DATABASE="20"     # 20 % of total (~4 GB)
GETH_CACHE_GC="12"           # 12 %
GETH_CACHE_SNAPSHOT="24"     # 24 %
GETH_CACHE_TRIE="44"         # 44 %

Scale GETH_CACHE proportionally to your host RAM:

GETH_CACHE="8192"            # 8 GB cache for a 16 GB host

3.7 Logging

Variable Default Values
OP_NODE_LOG_LEVEL info trace, debug, info, warn, error, crit
OP_NODE_LOG_FORMAT json json, logfmt, terminal
BASE_NODE_LOG_VERBOSITY 3 0–5
BASE_NODE_LOG_FORMAT json json, logfmt, terminal
LOG_LEVEL (reth entrypoint) info error, warn, info, debug, trace
GETH_VERBOSITY 3 0–5
NETHERMIND_LOG_LEVEL Info Off, Error, Warn, Info, Debug, Trace

3.8 Metrics

Prometheus metrics are enabled by default. Scrape:

  • Execution: http://<host>:7301/metrics
  • Consensus: http://<host>:7300/metrics

STATSD_ADDRESS="172.17.0.1" is also set but only used by base-consensus (best-effort StatsD push to the Docker host's bridge IP).

3.9 Optional Features (Commented-Out By Default)

EthStats Monitoring

OP_GETH_ETH_STATS=nodename:secret@host:port
# or for nethermind:
OP_NETHERMIND_ETHSTATS_ENABLED=true
OP_NETHERMIND_ETHSTATS_NODE_NAME=NethermindNode
OP_NETHERMIND_ETHSTATS_ENDPOINT=ethstats_endpoint

Trusted L1 Mode

OP_NODE_L1_TRUST_RPC=true

Caution

OP_NODE_L1_TRUST_RPC=true disables op-node's own validation of L1 responses. Only enable when pointing at an L1 RPC you fully control.

Snap Sync (Experimental)

OP_GETH_SYNCMODE=snap
OP_GETH_BOOTNODES=enode://...,enode://...
# or for nethermind:
OP_NETHERMIND_BOOTNODES=enode://...,enode://...

Warning

Snap sync on Base is experimental and may produce sync issues. Full sync is the default and recommended path.

Flashblocks (Reth Only)

# .env.mainnet
RETH_FB_WEBSOCKET_URL=wss://mainnet.flashblocks.base.org/ws

# .env.sepolia
RETH_FB_WEBSOCKET_URL=wss://sepolia.flashblocks.base.org/ws

Flashblocks is Base's sub-block pending-block preview feature — see Section 5.7 for usage.

Pruning (Reth Only)

RETH_PRUNING_ARGS="--prune.senderrecovery.distance=50000 --prune.transactionlookup.distance=50000 --prune.receipts.distance=50000 --prune.accounthistory.distance=50000 --prune.storagehistory.distance=50000 --prune.bodies.distance=50000"

Caution

Pruning distance must be greater than 10064. Published pruned snapshots use 1_339_200 (~31 days). Setting the distance below 10064 can break consensus participation. Node type (archive vs pruned) cannot be changed after initial sync.


4. Core Usage

4.1 Start The Node

docker compose up --build

--build rebuilds the client image when versions.env has changed.

4.2 Background Mode

docker compose up --build -d
docker compose logs -f

4.3 Stop

docker compose down

Chain data in HOST_DATA_DIR persists.

4.4 Stop And Wipe Data

docker compose down
rm -rf ./geth-data ./reth-data ./nethermind-data   # whichever you used

4.5 Verify Execution Client Sync

curl -s -X POST http://localhost:8545 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
{"jsonrpc":"2.0","id":1,"result":"0x1a3b4c5"}

A non-0x0 result means the execution client is syncing or synced.

4.6 Verify Consensus Client

curl -s -X POST http://localhost:7545 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"optimism_syncStatus","params":[],"id":1}'

4.7 Check Peer Count

curl -s -X POST http://localhost:8545 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}'

Tip

If peer count stays at "0x0", your P2P ports are blocked. Verify 30303/tcp+udp (execution) and 9222/tcp+udp (consensus) are reachable from the internet using nc -vuz <your-public-ip> 30303 from outside your network.

4.8 Follow Logs

docker compose logs -f                # both containers
docker compose logs -f execution      # execution client only
docker compose logs -f node           # consensus client only

4.9 Inspect Inside A Container

docker compose exec execution bash
docker compose exec node bash

4.10 Restart One Service

docker compose restart execution
docker compose restart node

4.11 Query Metrics

curl -s http://localhost:7301/metrics | head -n 20      # execution
curl -s http://localhost:7300/metrics | head -n 20      # consensus

4.12 Check Disk Usage

du -sh ./geth-data ./reth-data ./nethermind-data 2>/dev/null
df -h .

5. Common Tasks

5.1 Recipe — Start A Mainnet Reth Archive Node

Goal: Get the recommended Coinbase production configuration up and syncing.

Prerequisites:

  • L1 endpoints configured in .env.mainnet
  • Disk room for 2 × current chain size + snapshot size + 20 %
  • Ports 30303/tcp+udp and 9222/tcp+udp open in your firewall

Steps:

# Pick reth and use base-consensus (the default for the shipped .env)
export CLIENT=reth
export HOST_DATA_DIR=./reth-data

docker compose up --build -d
docker compose logs -f

Verify:

curl -s -X POST http://localhost:8545 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

A result field other than "0x0" confirms reth is producing block numbers.

If it fails: check docker compose logs execution for the reth boot output. The most common first-time failure is missing or wrong L1 endpoints — see Section 6.

5.2 Recipe — Sync To Current Head Quickly

Goal: Skip days of genesis sync by restoring from a published snapshot.

Steps:

# 1. Stop any running stack
docker compose down

# 2. Identify your snapshot:
#    Visit https://basechaindata.vercel.app and pick the URL that matches
#    your client (reth/geth/nethermind) and network (mainnet/sepolia).
SNAPSHOT_URL="https://example/path/to/snapshot.tar.zst"

# 3. Download and extract into HOST_DATA_DIR
mkdir -p ./reth-data
curl -L "$SNAPSHOT_URL" | tar --use-compress-program=zstd -xf - -C ./reth-data

# 4. Start the stack
CLIENT=reth docker compose up --build -d

Verify: eth_blockNumber should jump immediately to the snapshot's height.

5.3 Recipe — Expose RPC To A Specific Subnet

Goal: Make the JSON-RPC available to your VPC peers but not the public internet.

Steps: edit docker-compose.yml and rebind the published ports to a private interface:

services:
  execution:
    ports:
      - "10.0.0.5:8545:8545"
      - "10.0.0.5:8546:8546"
      - "10.0.0.5:7301:6060"
      - "30303:30303"
      - "30303:30303/udp"

Then docker compose up -d.

Warning

Do not expose port 8551 (Engine API). It is JWT-authenticated, but the shipped JWT is a public sample value — anyone who reaches 8551 with the shipped key can drive your execution client.

5.4 Recipe — Switch From Mainnet To Sepolia

Goal: Run the Sepolia testnet variant on the same host (or move from mainnet to Sepolia).

Prerequisites: different HOST_DATA_DIR for Sepolia data.

Steps:

docker compose down

# Use a separate data directory for Sepolia
HOST_DATA_DIR=./reth-data-sepolia \
NETWORK_ENV=.env.sepolia \
CLIENT=reth \
docker compose up --build -d

Warning

Do not point the same HOST_DATA_DIR at both mainnet and Sepolia. The two chain databases are incompatible — you will corrupt the on-disk state.

5.5 Recipe — Snapshot And Restore

Goal: Take a backup and restore later (or move to another host).

Snapshot:

# Stop first — never back up while the node is running
docker compose down

# Compress the chain data
tar --use-compress-program='zstd -T0' -cf base-data.tar.zst ./reth-data

# Optional: ship to S3 or another backup target
aws s3 cp base-data.tar.zst s3://my-bucket/base/

Restore:

# Stop first
docker compose down

# Replace the data dir
rm -rf ./reth-data
mkdir ./reth-data
tar --use-compress-program=zstd -xf base-data.tar.zst -C .

# Start
docker compose up --build -d

Warning

Backups must be taken with the node stopped. Hot-backing-up an open MDBX (reth), LevelDB (geth), or Nethermind store will produce an inconsistent and unusable snapshot.

5.6 Recipe — Upgrade To A New Release

Goal: Pick up new pinned versions of op-node, op-geth, base-reth-node, or nethermind.

Steps:

# Pull the new pins
git pull

# Rebuild from scratch — the Dockerfiles re-clone the upstream repos at the new pinned commits
docker compose build --no-cache

# Start
docker compose up -d

Verify: docker compose logs execution | head shows the new client version. Each Dockerfile asserts that the cloned upstream's HEAD matches the pinned commit in versions.env; the build fails fast if a tag has been re-pointed upstream (supply-chain safety check).

5.7 Recipe — Enable Flashblocks (Reth)

Goal: Get sub-block pending-block previews on RPC.

Steps: uncomment in your env file and restart:

# .env.mainnet
RETH_FB_WEBSOCKET_URL=wss://mainnet.flashblocks.base.org/ws
docker compose down
CLIENT=reth docker compose up --build

You should see in the logs:

Enabling Flashblocks support with endpoint: wss://mainnet.flashblocks.base.org/ws

Query a pending Flashblock:

curl -X POST http://localhost:8545 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["pending", false],"id":1}'

Note

Flashblocks is reth-only. Setting RETH_FB_WEBSOCKET_URL with CLIENT=geth or CLIENT=nethermind has no effect. If RETH_FB_WEBSOCKET_URL is unset, reth runs in vanilla mode and prints Running in vanilla node mode (no Flashblocks URL provided).

5.8 Recipe — Monitor Health From Prometheus

Goal: Get sync lag, peer counts, and disk pressure on a dashboard.

Prometheus scrape config:

scrape_configs:
  - job_name: 'base-execution'
    static_configs:
      - targets: ['my-base-host:7301']
  - job_name: 'base-consensus'
    static_configs:
      - targets: ['my-base-host:7300']

Suggested alerts:

Alert Threshold
Sync lag Node head trails sequencer head by > 30s for 5 min
Peer count drop < 5 execution peers or < 3 consensus peers for 5 min
Engine API errors Non-200 response rate from execution to consensus
Disk pressure node_filesystem_avail_bytes falls below 20 %
Restart loops Docker container restart count keeps increasing

Tip

The pprof endpoint on localhost:6060 (consensus container) is great for ad-hoc Go profiling: go tool pprof http://localhost:6060/debug/pprof/heap. Restrict its host binding in production — the pprof endpoint is unauthenticated.


6. Troubleshooting

Error: expected OP_NODE_NETWORK to be set

Cause: Compose is running without an env file, or the file doesn't define OP_NODE_NETWORK.

Solution: Ensure .env.mainnet (or .env.sepolia) exists and contains OP_NODE_NETWORK=base-mainnet (or base-sepolia). Launch with the right env file:

NETWORK_ENV=.env.mainnet docker compose up

Prevention: Don't delete lines from the shipped env files. Only fill in the <your-preferred-*> placeholders.


Error: expected BASE_NODE_NETWORK to be set

Cause: USE_BASE_CONSENSUS=true is set but the env file doesn't define BASE_NODE_NETWORK.

Solution: The shipped env files already define this (BASE_NODE_NETWORK=base for mainnet, base-sepolia for testnet). Confirm you are loading the correct file via NETWORK_ENV.


Error: Base client is not supported for this node type

Cause: USE_BASE_CONSENSUS=true with CLIENT=geth or CLIENT=nethermind. The base-consensus binary ships only in the reth image.

Solution: Either use CLIENT=reth or set USE_BASE_CONSENSUS=false:

CLIENT=reth USE_BASE_CONSENSUS=true  docker compose up --build
# or
CLIENT=geth USE_BASE_CONSENSUS=false docker compose up --build

Prevention: Treat the two variables as a pair. The shipped .env defaults USE_BASE_CONSENSUS=true, which only works with reth. See issue #1016.


Error: Could not retrieve public IP.

Cause: All four IP-echo providers (ifconfig.me, api.ipify.org, ipecho.net, v4.ident.me) failed from inside the container. The entrypoint exits with code 8.

Solution: Ensure outbound HTTP on port 80 from the container. If you are behind a strict egress firewall, allowlist at least one of those four hosts. The entrypoints do not currently accept a manual public-IP override.

Prevention: Confirm outbound internet works from inside Docker before launching:

docker run --rm curlimages/curl https://ifconfig.me

Error: waiting for execution client to be ready (repeats forever)

Cause: The consensus container is polling the execution container's Engine API port and not getting an HTTP 401 (which proves the execution client is up and rejecting unauthenticated calls). Either the execution container has crashed, or the engine RPC URL is wrong.

Solution: In another shell:

docker compose logs execution | tail -n 100

If the execution container has exited, check for startup errors (bad chain config, insufficient disk, corrupted database). The engine RPCs default to http://execution:8551 (or ws://execution:8551 on Sepolia) — only change them if you customized the compose file.


Error: Peer count stuck at 0x0

Cause: Inbound P2P traffic blocked. Without inbound connectivity, neither client can find peers.

Solution: Verify external reachability:

nc -vuz <your-public-ip> 30303
nc -vuz <your-public-ip> 9222

Open 30303/tcp+udp and 9222/tcp+udp in your router or cloud security group. Restart the stack.

Prevention: Document your port mappings before a long sync. Test with nc or nmap first.


Error: version downgrade detected: v0.3.0 -> v0.2.9

Cause: The dependency_updater CLI rejected an upgrade because the new version sorts lower than the current pinned tag. This is a CI/internal error from ValidateVersionUpgrade() in dependency_updater/version.go.

Solution: This is not a node runtime error — see the Admin Guide §8 for the dependency updater workflow. If you hit it running the tool manually, double-check the GitHub tag actually represents a newer release.


Error: Commit hash verification failed

Cause: During a Docker build, a clone step pinned an upstream tag, but the tag's HEAD commit no longer matches OP_NODE_COMMIT / BASE_RETH_NODE_COMMIT / NETHERMIND_COMMIT / OP_GETH_COMMIT in versions.env. Either the upstream tag was force-pushed, or your local versions.env is stale.

Solution:

git pull
docker compose build --no-cache

Prevention: Always run docker compose up --build after git pull. The pinned-commit assertion is a supply-chain safety net; don't disable it.


Error: reth sync stuck or extremely slow

Cause: First-time archive sync depends heavily on L1 provider throughput, especially blob fetching.

Solution:

  1. Use a snapshot — see Section 5.2.
  2. Use a high-throughput L1 beacon archiver that returns all historical blob sidecars.
  3. Confirm OP_NODE_L1_BEACON_FETCH_ALL_SIDECARS="true" (default in shipped env files).

Error: docker-compose: command not found

Cause: You are using the legacy v1 CLI syntax (hyphenated). Base's compose file requires Compose v2 features.

Solution: Use docker compose (two words):

docker compose up --build

If your install is too old, upgrade Docker Engine to 20.10+ with the compose plugin or install Docker Desktop. See issue #1012.


Error: unsupported architecture: <arch> during reth build

Cause: The reth Dockerfile downloads the mold linker for x86_64, aarch64/arm64, and armv7l/armv6l. Other architectures abort the build.

Solution: Build on a supported architecture or modify reth/Dockerfile to add a case for your CPU.


Error: OP_NODE_P2P_ADVERTISE_IP is wrong (NAT / CGNAT)

Cause: The public-IP auto-detect returns your NAT's address, which may not be externally reachable (typical with carrier-grade NAT).

Solution: Expose the node behind a routable IP or a port-forwarding gateway. The entrypoints do not currently accept a manual advertise-IP override; you'd need to patch op-node-entrypoint / base-consensus-entrypoint to export OP_NODE_P2P_ADVERTISE_IP=<your-routable-ip> before the exec.


Error: Disk fills up rapidly

Cause: Base archive data grows continuously. Reth archive can exceed 3 TB; geth full archive is similar.

Solution:

  • Watch disk: df -h.
  • Plan storage as 2 × current chain size + snapshot size + 20 %.
  • Consider pruning (reth, distance > 10064) or OP_GETH_GCMODE=full + OP_GETH_SYNCMODE=snap for geth.

Error: Ports 8545 / 8546 already in use

Cause: Another local Ethereum node, Anvil, or Hardhat is bound to those ports.

Solution: Stop the other process, or remap Base's ports in docker-compose.yml:

ports:
  - "18545:8545"
  - "18546:8546"

Error: rollup.sequencerhttp unreachable

Cause: Outbound HTTPS to https://mainnet-sequencer.base.org (or the Sepolia equivalent) is blocked.

Solution: Allow outbound HTTPS. In an air-gapped or egress-restricted environment, you would need to proxy or mirror the sequencer endpoint.


Error: Nethermind fails with Configuration file '<network>' not found

Cause: Nethermind launches with --config=$OP_NODE_NETWORK. The pinned nethermind build must contain a chainspec for that network.

Solution: Confirm versions.json has a current nethermind tag that supports Base:

git pull
docker compose build --no-cache

Error: Enabling historical proofs preflight… blocks for hours

Cause: When RETH_HISTORICAL_PROOFS=true and RETH_HISTORICAL_PROOFS_STORAGE_PATH is set, reth's preflight does a start-wait-stop-init dance that can take up to 6 hours on a fresh database.

Solution: Plan a long maintenance window the first time you enable historical proofs. After the initial init, subsequent boots are fast.

Warning

If RETH_HISTORICAL_PROOFS=true but RETH_HISTORICAL_PROOFS_STORAGE_PATH is empty, the preflight is silently skipped — historical proofs won't work, but the boot doesn't fail. Validate the path is set if you rely on this feature.


7. FAQ

Q: Which client should I pick?

Coinbase's production runs reth archive on mainnet — the best archive performance on modern NVMe hardware. Geth is the most battle-tested. Nethermind is the .NET option. Pick reth unless you have a specific operational reason to choose another.


Q: Do I need to run my own L1 Ethereum node?

No. Any reliable L1 RPC + beacon + beacon-archiver works (Alchemy, Infura, QuickNode, your own). Running your own L1 gives the best reliability and avoids rate limits, but is not required.


Q: How long does initial sync take?

From genesis: days to weeks, depending on client, hardware, and L1 throughput. From a snapshot: typically hours.


Q: Can I run both mainnet and Sepolia on the same machine?

Yes — use different HOST_DATA_DIR values and publish each stack on different host ports (edit docker-compose.yml). Run each stack from its own copy of the repo, or pass -p <project-name> to docker compose to keep container names separate.


Q: Can I migrate synced data from geth to reth (or vice versa)?

No. Each client uses a different on-disk format. To switch clients, fetch a fresh snapshot for the target client.


Q: What is "Flashblocks"?

Base's sub-block pending-block preview, delivered over a websocket. RPC clients can query the pending block before it's finalized. Supported only by reth in this repository — see Section 5.7.


Q: What does USE_BASE_CONSENSUS=true do?

It replaces the default Go-based op-node with Base's Rust-based base-consensus binary. It only works with CLIENT=reth because the base-consensus binary is built into the reth image only. It is the default in the shipped .env.


Q: Can I run with less than 32 GB RAM?

You can try, but the shipped Geth cache defaults assume 64 GB+. For smaller hosts, lower GETH_CACHE (MB total). Insufficient RAM usually shows up as OOM kills or extreme slowness.


Q: Where does the JWT secret come from?

From BASE_NODE_L2_ENGINE_AUTH_RAW in the env file (a hex string). The entrypoints write it to /tmp/engine-auth-jwt (BASE_NODE_L2_ENGINE_AUTH) at startup, and both the execution and consensus clients read from that file. Important: the shipped value is a public sample — generate your own with openssl rand -hex 32 if you ever expose port 8551.


Q: How do I upgrade to a new Base release?

git pull
docker compose build --no-cache
docker compose up -d

The pinned versions live in versions.env / versions.json and are bumped upstream by CI (the dependency_updater Go tool — see the Admin Guide).


Q: Is base/node the actual node software?

No. This repository contains Docker builds, env templates, and entrypoint scripts. The execution binary (reth, geth, nethermind), op-node, and base-consensus are all built from their own upstream repos at the pinned commit hashes recorded in versions.env.


Q: How do I enable Prometheus scraping?

Metrics are already exposed on port 7301 (execution) and 7300 (consensus). Point your Prometheus scrape config at http://<host>:7301/metrics and http://<host>:7300/metrics — see Section 5.8.


Q: Why does op-node show OP_NODE_SYNCMODE=execution-layer (deprecated)?

The setting is being phased out upstream but is still accepted by the pinned op-node version. Leave it alone until Base publishes a release that removes the warning.


Q: How do I report a bug?

Open an issue at github.com/base/node/issues, or post in the node-operators channel of the Base Discord.


Q: Does Base offer a bug bounty?

Yes — Coinbase's HackerOne program covers Base. Submit at hackerone.com/coinbase. Security issues should never be filed on GitHub.


Q: What pinned versions am I running?

At commit d4a32b2:

Component Tag
base_reth_node (built from base/base) v0.7.6
nethermind (NethermindEth/nethermind) 1.36.2
op_geth (ethereum-optimism/op-geth) v1.101702.0
op_node (ethereum-optimism/optimism, prefix op-node) op-node/v1.16.11

Read versions.json for the canonical, version-controlled list including SHA-256 commits.