base/node
Everything required to run your own Base node
Mixed signals — read the receipts
- ✓Last commit 4d ago
- ✓5 active contributors
- ✓Distributed ownership (top contributor 37%)
- ✓MIT licensed
- ✓CI configured
- ⚠Small team — 5 top contributors
- ⚠No test directory detected
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Embed this verdict
[](https://repopilot.app/r/base/node)Paste into your README — the badge live-updates from the latest cached analysis.
Onboarding doc
Onboarding: base/node
Generated by RepoPilot · 2026-05-05 · Source
Verdict
WAIT — Mixed signals — read the receipts
- Last commit 4d ago
- 5 active contributors
- Distributed ownership (top contributor 37%)
- MIT licensed
- CI configured
- ⚠ Small team — 5 top contributors
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
TL;DR
base/node provides Docker-based infrastructure to run a self-hosted Base L2 node on the OP Stack. It packages three execution client options (reth, geth, nethermind) alongside the op-node consensus client via docker-compose, wiring together L1 RPC connections and beacon chain endpoints so operators can participate in or archive the Base network without relying on Coinbase-hosted infrastructure. Flat repo with one docker-compose.yml at root orchestrating three client subdirectories (geth/, reth/, nethermind/), each containing a Dockerfile and shell entrypoint script. The dependency_updater/ is a self-contained Go module that automates version bumps. Network config is split across .env, .env.mainnet, and .env.sepolia, with supervisord.conf managing process supervision inside containers.
Who it's for
Infrastructure engineers and blockchain developers who need to run their own Base L2 archive or full node — either for dApp backends requiring trusted RPC access, MEV research, data indexing, or decentralization goals — and want a turnkey Docker setup rather than building from source manually.
Maturity & risk
The repo has active CI via .github/workflows/ (docker build, PR checks, stale issue management, automated dependency updates), versioned configuration in versions.env and versions.json, and supports three production execution clients. The dependency_updater Go tool and the AWS i7i.12xlarge production hardware specs signal real production usage. Verdict: production-ready and actively maintained.
The repo depends on upstream github.com/ethereum-optimism/optimism v1.13.3 and tracks external client versions (reth, geth, nethermind) via versions.env/versions.json — a lag in updating these can cause chain sync failures after network upgrades. The dependency_updater tool automates this via .github/workflows/update-dependencies.yml, reducing but not eliminating version drift risk. Node operators also carry full responsibility for L1 RPC reliability, which is a hard external dependency not provided by this repo.
Active areas of work
Active work is visible in automated dependency tracking via dependency_updater/dependency_updater.go and versions.json, Flashblocks mode support for reth (documented in reth/README.md via RETH_FB_WEBSOCKET_URL), and the multi-client support expansion (nethermind added alongside geth and reth). CI workflows for Docker image publishing and PR validation are actively maintained.
Get running
git clone https://github.com/base/node.git && cd node cp .env.mainnet .env
Edit .env and set:
OP_NODE_L1_ETH_RPC=<your-l1-rpc>
OP_NODE_L1_BEACON=<your-beacon-endpoint>
OP_NODE_L1_BEACON_ARCHIVER=<your-beacon-archiver>
docker compose up --build
Or for testnet with reth:
NETWORK_ENV=.env.sepolia CLIENT=reth docker compose up --build
Daily commands:
Mainnet with default reth client:
docker compose up --build
Sepolia testnet:
NETWORK_ENV=.env.sepolia docker compose up --build
Specific client:
CLIENT=geth docker compose up --build CLIENT=nethermind docker compose up --build
Run dependency updater (Go 1.24.3 required):
cd dependency_updater && go run . --help
Map of the codebase
docker-compose.yml— The primary orchestration file that wires together all node services (op-node, geth/reth/nethermind, and consensus client) — the entry point for running the entire stack.versions.env— Defines the pinned Docker image versions for every component; changing versions here drives the entire upgrade lifecycle.op-node-entrypoint— Shell script that configures and launches the OP rollup node (op-node), responsible for deriving L2 blocks from L1 data.geth/geth-entrypoint— Shell entrypoint for the execution layer (go-ethereum); sets all critical flags including data dir, network config, and authenticated RPC..env.mainnet— Canonical mainnet configuration supplying L1 RPC endpoints and network-specific parameters that operators must customize before running.dependency_updater/dependency_updater.go— Core logic for the automated dependency update tool that queries GitHub releases and bumps versions across the repo.supervisord.conf— Process supervisor configuration that manages startup order and restart policies for all services within a single container deployment.
How to make changes
Add a new execution client (EL) option
- Create a new directory and Dockerfile for the client, following the pattern of existing clients (multi-stage build, copy binary, set entrypoint). (
nethermind/Dockerfile) - Write a shell entrypoint script that reads environment variables and passes the correct flags to the client binary. (
nethermind/nethermind-entrypoint) - Add a new service block in docker-compose.yml referencing the new image, mount the JWT secret volume, and link it to the op-node service via authrpc. (
docker-compose.yml) - Add the new client's image version variable to both versions files so it participates in automated dependency updates. (
versions.env)
Upgrade a pinned component version
- Update the relevant version variable (e.g. OP_GETH_IMAGE_VERSION) in versions.env to the new release tag. (
versions.env) - Mirror the same change in versions.json so the dependency_updater tool's state stays consistent. (
versions.json) - If the updater tool's known-versions list needs updating (e.g. new repo added), add a new entry in dependency_updater.go's component definitions. (
dependency_updater/dependency_updater.go)
Add support for a new network (e.g. a new testnet)
- Create a new .env file (e.g. .env.holesky) with the network-specific OP_NODE_L1_ETH_RPC, L1 beacon, and genesis/rollup config URLs. (
.env.sepolia) - Update the op-node-entrypoint to handle any new network-specific flags or genesis file paths that differ from existing networks. (
op-node-entrypoint) - Update README.md with quick-start instructions for the new network, referencing the new .env file. (
README.md)
Add or modify a node startup flag
- Identify which entrypoint script owns the flag (op-node vs execution client) and add the new flag, gating it with an env variable check for operator override. (
op-node-entrypoint) - Add the corresponding environment variable with a sensible default to the base .env template. (
.env) - Document the new variable in the README's configuration section. (
README.md)
Why these technologies
- Docker Compose — Provides a single-command, reproducible way for node operators to run a multi-process stack without deep Kubernetes or infrastructure knowledge.
- op-geth (go-ethereum fork) — The canonical execution client for OP Stack; inherits Ethereum's battle-tested EVM while adding OP-specific deposit transaction handling.
- reth (Rust EL client) — Offered as a high-performance alternative EL with lower memory footprint and faster sync times, giving operators client diversity.
- op-node (Go rollup node) — Implements the OP Stack rollup derivation pipeline, translating L1 data into L2 blocks via the Engine API — the core of the OP Stack design.
- Go (dependency_updater) — Matches the broader OP Stack ecosystem toolchain and provides strong typing for version semver parsing and GitHub API interaction.
Trade-offs already made
-
Multiple execution client options (geth, reth, nethermind)
- Why: Reduces systemic risk from single-client bugs and allows operators to optimise for their hardware/performance profile.
- Consequence: Increased maintenance surface; each client needs its own Dockerfile and entrypoint script to be kept in sync.
-
Shell scripts for entrypoints rather than a config file DSL
- Why: Shell scripts are universally understood, easy to override, and can conditionally set flags based on environment variables without a custom config parser.
- Consequence: Shell scripts are harder to unit test and prone to quoting/escaping bugs; no static type checking on configuration values.
-
Pinning all versions in versions.env and automating updates via dependency_updater
- Why: Ensures reproducible builds and safe, auditable upgrades through PRs rather than implicit 'latest' tag pulls.
- Consequence: Operators running from source must manually update or run the updater; older pinned versions may lag behind security patches briefly.
-
supervisord for single-container multi-process mode
- Why: Allows the entire stack to run as a single container for simpler deployments where docker-compose is unavailable.
- Consequence: Adds a Python/supervisord dependency to the image and complicates log aggregation compared to per-container stdout.
Non-goals (don't propose these)
- Running a sequencer or proposer — this repo is read-only/replica node infrastructure only.
- Kubernetes or Helm chart deployment — only Docker Compose is provided.
- Custom block production or MEV infrastructure.
- L
Traps & gotchas
- You MUST provide all three L1 env vars (
OP_NODE_L1_ETH_RPC,OP_NODE_L1_BEACON,OP_NODE_L1_BEACON_ARCHIVER) — missing any will cause op-node to fail silently or refuse to start. 2. The CLIENT env var defaults torethif unset, but the docker-compose conditional logic means specifying an unsupported CLIENT value may silently fall through to a wrong container. 3. Storage requirements are non-trivial and dynamic — the README links to live chain size stats; under-provisioning NVMe will cause node crashes without obvious error messages. 4.OP_NODE_L1_RPC_KINDdefaults todebug_geth— if using Alchemy or QuickNode, this must be changed or certain RPC calls will fail. 5. Thedependency_updaterGo module is a separate module (go.modindependency_updater/) and cannot be built from the repo root.
Architecture
Concepts to learn
- OP Stack derivation pipeline — The op-node implements L2 block derivation from L1 data — understanding this pipeline explains why both L1 RPC and beacon endpoints are mandatory for the node to function.
- Execution-Consensus split (Engine API) — Base nodes use a separated execution client (reth/geth/nethermind) and consensus client (op-node) communicating over the Engine API — this is why two processes must run together, not one monolithic binary.
- L1 Beacon archiver (EIP-4844 blob data) — op-node requires
OP_NODE_L1_BEACON_ARCHIVERto access historical blob data (EIP-4844) that standard beacon nodes prune after ~18 days, making this endpoint critical for syncing from genesis or after a gap. - supervisord process supervision — supervisord.conf controls how op-node and the execution client are co-managed inside the container, affecting restart behavior and log routing — misunderstanding this causes confusion when diagnosing crashes.
- Flashblocks (pre-confirmation blocks) — reth/README.md and reth-entrypoint expose RETH_FB_WEBSOCKET_URL for Flashblocks mode, a Base-specific feature providing sub-block pre-confirmation data streams not present in standard reth.
- Semver-based automated dependency updates — dependency_updater/dependency_updater.go uses Masterminds/semver to parse and compare client release tags on GitHub, enabling automated PRs when new client versions are published — the mechanism that keeps versions.env/versions.json current.
Related repos
ethereum-optimism/optimism— Upstream OP Stack monorepo that provides the op-node binary and L2 protocol that base/node depends on and tracks.paradigmxyz/reth— The default execution client used by base/node; understanding reth's CLI flags is essential for tuning the reth-entrypoint.ethereum-optimism/op-geth— The Base-patched geth fork used in the geth/ client option, diverging from upstream go-ethereum with OP Stack modifications.ethereum/go-ethereum— Upstream go-ethereum (geth) whose patterns and flags inform the geth/geth-entrypoint configuration.base-org/node-snapshots— Companion snapshot service referenced in the README for seeding initial chain data to avoid syncing from genesis.
PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add unit tests for dependency_updater/version.go alongside the existing version_test.go
The repo already has dependency_updater/version_test.go, meaning a test harness exists, but the dependency_updater only has one test file covering version logic. The version.go file parses and compares semver strings from versions.json and versions.env — edge cases like pre-release tags, malformed version strings, and cross-file consistency (versions.env vs versions.json) are likely untested. Adding thorough table-driven tests directly improves the reliability of the automated dependency update workflow triggered by .github/workflows/update-dependencies.yml.
- [ ] Read dependency_updater/version.go and dependency_updater/version_test.go to understand current coverage gaps
- [ ] Add table-driven tests in version_test.go covering: malformed semver strings, pre-release/build-metadata versions, empty string inputs, and version comparison edge cases (equal, greater, lesser)
- [ ] Add a test that loads both versions.env and versions.json fixture files and asserts they reference the same set of component keys (geth, op-node, reth, nethermind, etc.) to prevent drift between the two files
- [ ] Run
go test ./...inside dependency_updater/ and ensure all new cases pass - [ ] Update the PR workflow in .github/workflows/pr.yml if it does not already run the Go test suite for the dependency_updater module
Add a GitHub Actions workflow to lint and validate all entrypoint shell scripts (geth-entrypoint, reth-entrypoint, nethermind-entrypoint, op-node-entrypoint, consensus-entrypoint)
The repo contains at least six shell entrypoint scripts (geth/geth-entrypoint, reth/reth-entrypoint, nethermind/nethermind-entrypoint, op-node-entrypoint, base-consensus-entrypoint, consensus-entrypoint) that are the critical runtime paths for every node operator. None of the existing workflows in .github/workflows/ (docker.yml, pr.yml, stale.yml, update-dependencies.yml) run a shell linter like ShellCheck against these files. A ShellCheck workflow would catch unquoted variables, missing set -euo pipefail, and other bugs before they reach node operators.
- [ ] Create .github/workflows/shellcheck.yml that triggers on pull_request and push to main
- [ ] Use the
koalaman/shellcheck-actionor a directshellcheckinstall step to lint all entrypoint files: geth/geth-entrypoint, reth/reth-entrypoint, nethermind/nethermind-entrypoint, op-node-entrypoint, base-consensus-entrypoint, consensus-entrypoint - [ ] Fix any existing ShellCheck warnings found in those files (e.g. add
set -euo pipefail, quote variables referencing .env values like $OP_NODE_L1_ETH_RPC) - [ ] Add the new workflow badge to README.md so contributors can see its status
- [ ] Document in CONTRIBUTING.md that new entrypoint scripts must pass ShellCheck before merging
Add a docker-compose validation and .env variable completeness check to the PR workflow
The repo ships three environment files (.env, .env.mainnet, .env.sepolia) and a docker-compose.yml, but .github/workflows/pr.yml does not verify that (1) docker-compose.yml is syntactically valid, or (2) every variable referenced with ${VAR} in docker-compose.yml and the entrypoint scripts is declared in both .env.mainnet and .env.sepolia. Missing or renamed variables are a common source of node startup failures for operators. A lightweight CI step using docker compose config and a small grep/diff script would catch these issues on every PR.
- [ ] In .github/workflows/pr.yml, add a job step
Good first issues
- Add unit tests for
dependency_updater/version.goedge cases (pre-release semver tags, RC versions) —version_test.goexists but coverage of edge cases is likely incomplete given the minimal file size. 2. Add ahealthcheckdirective to each service indocker-compose.ymlsodocker compose pscan report actual node sync status rather than just container running state. 3. Document thesupervisord.confprocess configuration inREADME.md— it's a non-obvious operational detail that affects how logs are accessed and how crashes are handled in production.
Top contributors
- @github-actions[bot] — 21 commits
- @danyalprout — 14 commits
- @JenabaBa — 11 commits
- @meyer9 — 7 commits
- @wlawt — 4 commits
Recent commits
77255d1— fix: default CLIENT to reth in .env and docker-compose.yml (#1035) (mw2000)d4a32b2— fix: disable NuGet audit in nethermind Dockerfile (#1036) (mw2000)3bac950— fix(reth): replace deprecated docker-compose with docker compose (#1013) (Sertug17)ca91ddb— bump: base/base to 0.7.6 (#1027) (danyalprout)8e10bf3— fix: 0.7.5 (#1026) (refcell)726a616— bump: base/base to 0.7.4 (#1025) (refcell)e493aa8— bump: base/base to 0.7.3 (#1021) (refcell)9480ec1— fix: add legacy op node flag (#1002) (meyer9)0ef36d3— fix: switch all entrypoints to use base node auth (#1001) (meyer9)c8d0862— chore: updated base, op-geth, optimism (#995) (github-actions[bot])
Security observations
- High · Sensitive .env Files Tracked in Repository —
.env, .env.mainnet, .env.sepolia. The file structure explicitly lists .env, .env.mainnet, and .env.sepolia as tracked files in the repository. These files likely contain sensitive configuration such as L1 RPC endpoints, private keys, JWT secrets, or API keys. If the repository is public or accidentally exposed, these credentials could be compromised. The README confirms these files contain OP_NODE_L1_ETH_RPC and related endpoint configurations. Fix: Remove .env files from version control immediately. Add .env and .env. patterns to .gitignore (keeping .env.example templates only). Use environment variable injection at runtime via CI/CD secrets, Docker secrets, or a secrets manager (e.g., HashiCorp Vault, AWS Secrets Manager). Rotate any credentials that may have been exposed. - High · RPC Ports Exposed on All Network Interfaces —
docker-compose.yml. In docker-compose.yml, the execution client binds RPC port 8545 and WebSocket port 8546 to 0.0.0.0 (all interfaces) by default via the port mappings '8545:8545' and '8546:8546'. This exposes the Ethereum JSON-RPC and WebSocket APIs to the public internet if the host firewall is not properly configured, allowing unauthorized parties to query blockchain state, submit transactions, or potentially exploit RPC vulnerabilities. Fix: Bind RPC ports to localhost only by changing port mappings to '127.0.0.1:8545:8545' and '127.0.0.1:8546:8546'. Use a reverse proxy (e.g., nginx with TLS and authentication) for any externally accessible RPC. Apply host-level firewall rules (iptables/ufw) to restrict access to trusted IP ranges. - High · Metrics and pprof Ports Exposed Publicly —
docker-compose.yml (ports: 7301:6060, 6060:6060, 7300:7300). Prometheus metrics port 7301 (mapped to internal 6060) and pprof port 6060 are exposed in docker-compose.yml without interface binding restrictions. The pprof endpoint in particular can leak sensitive runtime information (goroutine dumps, heap profiles, CPU profiles) and in some cases can be exploited for denial-of-service. Metrics endpoints can leak operational intelligence about the node. Fix: Bind metrics and pprof ports to localhost: '127.0.0.1:7301:6060', '127.0.0.1:6060:6060', '127.0.0.1:7300:7300'. Expose metrics to Prometheus scraper via a private network or VPN. Consider disabling pprof in production deployments entirely. - Medium · Docker Build Context Includes Potentially Sensitive Files —
docker-compose.yml, .dockerignore, geth/Dockerfile, reth/Dockerfile, nethermind/Dockerfile. The docker-compose.yml uses the repository root as the Docker build context ('context: .') for all client Dockerfiles (geth, reth, nethermind). Without a comprehensive .dockerignore, sensitive files such as .env files, credentials, or private configuration may be inadvertently copied into Docker image layers, where they could be extracted from the image. Fix: Audit the .dockerignore file to ensure .env, .env.*, *.key, *.pem, and other sensitive files are explicitly excluded. Follow the principle of minimal build context. Scan Docker images with tools like Trivy or docker-scout for secrets in layers. - Medium · No Docker Image Digest Pinning —
geth/Dockerfile, reth/Dockerfile, nethermind/Dockerfile. The Dockerfiles and dependency configuration do not appear to pin base images using SHA256 digests. Using mutable tags (e.g., 'latest' or version tags) for base images allows a supply chain attack where a compromised upstream image could introduce malicious code into the node software without detection. Fix: Pin all Docker base images using immutable SHA256 digests (e.g., FROM ethereum/client-go@sha256:<hash>). Implement automated dependency scanning via tools like Dependabot or Renovate for Docker image updates with digest verification. - Medium · Shell Entrypoint Scripts Without Input Validation —
undefined. Multiple shell-based entrypoint scripts ( Fix: undefined
LLM-derived; treat as a starting point, not a security audit.
Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.