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_DIRbefore 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, andOP_NODE_L1_BEACON_ARCHIVER(and theBASE_NODE_*equivalents if you useUSE_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.12xlargeinstances 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. Usedocker compose(two words) — issue #1012 tracked the same fix in this repo'sreth/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 togeth(${CLIENT:-geth}). The README mentions reth as the recommended client, but the compose default is geth — track issue #1010. Be explicit aboutCLIENTin 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-consensusis shipped only in the reth image. SettingUSE_BASE_CONSENSUS=truewithCLIENT=gethorCLIENT=nethermindcauses the consensus container to exit withBase client is not supported for this node type. PairUSE_BASE_CONSENSUS=truewithCLIENT=reth, or setUSE_BASE_CONSENSUS=falsefor 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:
.env— host-side defaults consumed bydocker composeitself. ControlsCLIENT,HOST_DATA_DIR, andUSE_BASE_CONSENSUS..env.mainnetor.env.sepolia— loaded into both containers viaenv_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, orv4.ident.meand advertise it to peers.
Caution
All published RPC and metrics ports bind to
0.0.0.0inside 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 indocker-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=truedisables 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 use1_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. Verify30303/tcp+udp(execution) and9222/tcp+udp(consensus) are reachable from the internet usingnc -vuz <your-public-ip> 30303from 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+udpand9222/tcp+udpopen 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_DIRat 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_URLwithCLIENT=gethorCLIENT=nethermindhas no effect. IfRETH_FB_WEBSOCKET_URLis unset, reth runs in vanilla mode and printsRunning 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:
- Use a snapshot — see Section 5.2.
- Use a high-throughput L1 beacon archiver that returns all historical blob sidecars.
- 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=snapfor 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=truebutRETH_HISTORICAL_PROOFS_STORAGE_PATHis 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.