This guide is for node operators, infrastructure engineers, protocol developers, and architects who deploy, operate, secure, or extend Geth. It documents the system architecture, the complete configuration surface, deployment and operations procedures, security hardening, and the internal package design — written from the go-ethereum source at commit f5c62d0 (Go 1.24).
For installation and day-to-day usage, see the companion User Guide. This guide assumes familiarity with Ethereum concepts (accounts, transactions, the EVM, proof-of-stake) and focuses on how Geth is built, wired together, and run in production.
1. Executive Summary
Geth is the official Go implementation of an Ethereum execution-layer (EL) client. It is one process in a two-client node: Geth executes transactions and maintains state, while a separate consensus-layer (CL) client drives proof-of-stake fork choice. The two communicate over the authenticated Engine API.
| Aspect | Detail |
|---|---|
| Language / toolchain | Go 1.24, cgo (secp256k1, blst) |
| Role | Ethereum execution-layer client |
| Primary binary | geth (cmd/geth) |
| Companion tools | abigen, evm, devp2p, ethkey, era, rlpdump, abidump, blsync, workload, fetchpayload |
| CLI framework | urfave/cli/v2 |
| Storage engines | Pebble (default) or LevelDB, plus an "ancient" freezer and Era archives |
| State model | Merkle-Patricia trie over a path-based or hash-based node store; experimental binary trie |
| Networking | devp2p (discovery v4/v5, RLPx transport, eth/snap wire protocols) |
| External interfaces | JSON-RPC (HTTP/WS/IPC), GraphQL, Engine API, metrics |
| License | GNU LGPL-3.0 (library) / GPL-3.0 (binaries) |
Important
The single most consequential operational fact: a Geth process is not self-sufficient. Production deployment is always "Geth + a CL client + a shared JWT secret." Plan capacity, monitoring, and incident response around the pair, not Geth in isolation.
2. Architecture
2.1 High-level component diagram
┌──────────────────────────────────────────────┐
│ cmd/geth (CLI) │
│ urfave/cli app · flag parsing · subcommands │
└───────────────────────┬──────────────────────┘
│ builds
▼
┌────────────────────────────────────────────────────────────────────────┐
│ node.Node │
│ Service container: lifecycle, account manager, P2P server, │
│ RPC stack (IPC/HTTP/WS/Auth), GraphQL, datadir/keystore resolution │
└───────┬───────────────────────────────┬───────────────────────┬─────────┘
│ registers │ exposes │ auth port
▼ ▼ ▼
┌─────────────────────────┐ ┌──────────────────────┐ ┌──────────────────┐
│ eth.Ethereum │ │ rpc + internal/ │ │ eth/catalyst │
│ (the EL service) │ │ ethapi + graphql │ │ Engine API impl │
│ │ │ JSON-RPC / GraphQL │ │ (CL <-> EL) │
│ ┌───────────────────┐ │ └──────────────────────┘ └──────────────────┘
│ │ core.BlockChain │ │
│ │ · state processor│ │ ┌──────────────────────┐ ┌──────────────────┐
│ │ · core/vm (EVM) │ │ │ eth/downloader │ │ miner (payload │
│ │ · consensus │ │◄────►│ snap / full sync │ │ building) │
│ └───────────────────┘ │ └──────────────────────┘ └──────────────────┘
│ ┌───────────────────┐ │
│ │ core/txpool │ │ ┌──────────────────────────────────────────┐
│ │ legacy + blob │ │ │ eth/protocols (eth, snap) · eth/fetcher │
│ └───────────────────┘ │◄────►│ wire protocols over p2p │
└───────────┬─────────────┘ └──────────────────────────────────────────┘
│ reads/writes
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ State & storage: core/state (StateDB) → trie → triedb (hashdb|pathdb) │
│ → ethdb (pebble|leveldb) + core/rawdb (schema) + freezer/ancient + era │
└──────────────────────────────────────────────────────────────────────────┘
▲
│ transport
┌───────────┴──────────────────────────────────────────────────────────────┐
│ p2p: discover(v4/v5) · enode/enr · rlpx · dnsdisc · nat · netutil │
└────────────────────────────────────────────────────────────────────────────┘
2.2 The node service container
node.Node (package node) is the lifecycle and resource container that everything else plugs into. It owns:
- Datadir and keystore resolution — resolves the data directory, keystore path, and node key; manages the instance directory lock.
- P2P server — the devp2p
p2p.Serverthat handles discovery and peer connections. - The account manager (
accounts.Manager) — aggregates keystore, USB, and smartcard backends. - The RPC stack — IPC, HTTP, WebSocket, and the separate authenticated (Engine API) server, plus optional GraphQL. Default hosts/ports come from
node.DefaultConfig:
| Endpoint | Default host | Default port | Default modules |
|---|---|---|---|
| HTTP | localhost |
8545 |
net,web3 |
| WebSocket | localhost |
8546 |
net,web3 |
| Auth (Engine API) | localhost |
8551 |
eth,engine |
| IPC | — (geth.ipc in datadir) |
— | all local |
Services register with the node and are started/stopped in dependency order. cmd/geth's makeFullNode wires eth.Ethereum and its satellites into the node.
2.3 The eth execution service
eth.Ethereum (package eth) is the main EL service. It composes:
core.BlockChain— the canonical chain, block insertion/validation, the state processor that runs each block through the EVM, and reorg handling.core/vm— the Ethereum Virtual Machine: opcode interpreter, gas accounting, and precompiled contracts.core/txpool— the transaction mempool, split into the legacy pool (legacypool) for normal transactions andblobpoolfor EIP-4844 blob transactions.eth/downloader— orchestrates snap and full sync.eth/protocols/ethandeth/protocols/snap— the wire protocols spoken to peers;eth/fetcherpulls announced blocks and transactions.eth/filters— log/event filtering backingeth_getLogsand subscriptions, accelerated bycore/filtermapslog indexes.eth/gasprice— the gas-price oracle backing fee suggestions.eth/tracers— transaction tracing engines (native Go tracers and JavaScript tracers viagoja).miner— block/payload construction, invoked when the CL requests a payload via the Engine API.
eth/ethconfig defines the service's configuration struct and its defaults (sync mode, cache splits, txpool sizing, GPO parameters, RPC caps, history retention).
2.4 Consensus and the Engine API
Geth supports multiple consensus engines behind the consensus.Engine interface:
| Package | Engine | Use |
|---|---|---|
consensus/beacon |
Beacon (post-merge) | Wraps an inner engine; defers fork choice to the CL via the Engine API. The production path for mainnet. |
consensus/clique |
Clique | Proof-of-authority, used by --dev and private PoA networks. |
consensus/ethash |
Ethash | Legacy proof-of-work verification (historical blocks); not used for live mining. |
consensus/misc |
— | Shared fork rules (EIP-1559 base fee, EIP-4844 blob fee). |
eth/catalyst implements the Engine API — the engine_forkchoiceUpdated, engine_newPayload, and payload-building methods the CL calls on the authenticated port. beacon/engine defines the shared request/response types. The beacon/ tree also contains a full beacon light client (beacon/light, beacon/blsync) that powers the blsync tool.
2.5 State and storage stack
State storage is layered from high-level abstraction down to bytes on disk:
core/state.StateDB ← accounts, balances, nonces, code, storage slots
│
▼
trie (Merkle-Patricia) ← cryptographic state commitment; trie/bintrie = experimental binary trie
│
▼
triedb ← node store with two schemes:
├─ hashdb ← legacy: nodes keyed by hash
└─ pathdb ← modern default: nodes keyed by path, online pruning, state history
│
▼
ethdb (KV interface) ← pebble (default) | leveldb | memorydb | remotedb
│
▼
core/rawdb ← concrete key schema; "ancient"/freezer for immutable old data
└─ eradb / internal/era ← Era archive format for historical blocks
triedb/pathdbis the largest single module in the codebase and the modern default (--state.scheme path). It supports efficient pruning and bounded state history.triedb/hashdbis the legacy hash-keyed scheme.- The freezer (ancient store) moves immutable old blocks out of the hot KV store into append-only flat files (
--datadir.ancient), dramatically shrinking the live database. - Era files (
--datadir.era,geth import-history/export-history/download-era) provide compact, verifiable archives of historical blocks.
2.6 Networking stack (p2p)
| Package | Responsibility |
|---|---|
p2p |
The peer server, connection lifecycle, protocol multiplexing |
p2p/discover |
Node discovery DHT — protocol v4 and v5 |
p2p/enode, p2p/enr |
Node identity and Ethereum Node Records |
p2p/rlpx |
The encrypted RLPx transport |
p2p/dnsdisc |
DNS-based node lists |
p2p/nat |
NAT traversal (UPnP, NAT-PMP, STUN, explicit ext-IP) |
p2p/netutil, p2p/msgrate, p2p/tracker |
IP restriction, peer message-rate estimation, request tracking |
core/forkid computes the fork identity used during the handshake so peers only connect to compatible chains.
2.7 Supporting subsystems
| Package | Role |
|---|---|
rpc |
The JSON-RPC server/client framework (codecs, subscriptions, batching) |
internal/ethapi |
Implementations of the eth/debug-namespace RPC methods |
graphql |
GraphQL schema and resolvers (plus GraphiQL UI) |
accounts |
Account abstraction; keystore, usbwallet, scwallet backends; abi for contract encoding |
signer |
Offline/structured signing helpers (fourbyte selector DB, storage, core types) |
ethclient |
A Go client library for talking to a node (used by tooling and simulated backends) |
metrics |
Instrumentation registry with Prometheus and InfluxDB exporters |
log |
Structured, leveled logging |
params |
Chain configurations, fork schedules, and protocol constants |
rlp |
Recursive Length Prefix serialization |
common |
Hashing, hex, big-int, LRU, and primitive types |
ethstats |
Optional reporting to an ethstats dashboard |
internal/debug |
pprof, the --verbosity/log flags, and the debug runtime namespace |
3. System Requirements
| Resource | Full (pruned) node | Archive node |
|---|---|---|
| CPU | 4+ cores | 8+ cores |
| RAM | 16 GB | 32 GB+ |
| Disk | Terabyte-class NVMe/SSD with headroom | Multi-terabyte NVMe |
| Disk type | NVMe/SSD required | NVMe required |
| Network | Stable broadband, port 30303 open (TCP+UDP) |
same |
Caution
Mechanical disks (HDD) are not viable for any sync mode. State access during sync and block import is random-IO heavy; an HDD will cause the node to fall permanently behind the chain head. This is the most common cause of "node never syncs" support cases.
Note
Geth auto-shuts-down when free disk falls below
--datadir.minfreedisk(MB). Provision generous headroom; chain data grows continuously even on a pruned node.
4. Configuration Mastery
Geth has roughly 250 command-line flags. Every flag has a matching GETH_<NAME> environment variable (via flags.AutoEnvVars), and the full effective configuration can be dumped and reloaded as TOML.
4.1 Configuration precedence and the TOML file
# Dump the effective configuration to TOML
geth dumpconfig --mainnet > geth.toml
# Run with a config file (flags still override individual values)
geth --config geth.toml --mainnet
Precedence (highest wins): explicit CLI flags → environment variables (GETH_*) → --config TOML file → built-in defaults.
Tip
For reproducible production deployments, generate a
geth.tomlwithdumpconfig, commit it to configuration management, and pass--config. Keep only environment-specific values (datadir, JWT path, RPC exposure) as CLI flags.
4.2 Data, database, and history
| Flag | Default | Purpose |
|---|---|---|
--datadir <dir> |
platform-specific | Databases and keystore root |
--datadir.ancient <dir> |
inside chaindata | Freezer (ancient) data root |
--datadir.era <dir> |
inside ancient/chain | Era1 history root |
--datadir.minfreedisk <MB> |
--cache.gc-derived |
Free-disk floor before auto-shutdown (0 = disabled) |
--keystore <dir> |
inside datadir | Keystore directory |
--db.engine <name> |
pebble |
Backing KV store: pebble or leveldb |
--remotedb <url> |
— | Connect to a remote database |
--state.scheme <scheme> |
path |
path (modern, prunable) or hash (legacy) |
--gcmode <mode> |
full |
full (prune state) or archive (retain all) |
--history.state <n> |
90,000 | Blocks of state history to retain (path scheme; 0 = all) |
--history.transactions <n> |
~1 year | Blocks of transaction index (0 = all) |
--history.logs <n> |
~1 year | Blocks of log-search index (0 = all) |
--history.logs.disable |
off | Do not maintain a log index |
--history.chain <mode> |
all |
Body/receipt retention: all, postmerge, postprague |
4.3 Sync and cache
| Flag | Default | Purpose |
|---|---|---|
--syncmode <mode> |
snap |
snap or full |
--snapshot |
enabled | Enable snapshot-database acceleration |
--cache <MB> |
1024 | Total memory for internal caching |
--cache.database <pct> |
— | Share of cache for DB IO |
--cache.trie <pct> |
15% full / 30% archive | Share for trie caching |
--cache.gc <pct> |
25% full / 0% archive | Share for trie pruning |
--cache.snapshot <pct> |
10% full / 20% archive | Share for snapshot caching |
--cache.noprefetch |
off | Disable heuristic state prefetch |
--cache.preimages |
off | Record keccak preimages of trie keys |
--fdlimit <n> |
system limit | Raise the open-file-descriptor limit |
Tip
On a memory-rich machine, raising
--cache(e.g.--cache 8192) materially speeds sync and block import. The percentage sub-flags rebalance how that budget is split; the defaults are sensible for full mode.
4.4 Networking and discovery
| Flag | Default | Purpose |
|---|---|---|
--port <n> |
30303 |
P2P listening port (TCP) |
--discovery.port <n> |
30303 |
UDP discovery port |
--maxpeers <n> |
50 | Max peers (0 disables networking) |
--maxpendpeers <n> |
(default) | Max pending connection attempts |
--bootnodes <enodes> |
network default | Bootstrap enode URLs |
--nat <spec> |
any |
any|none|upnp|pmp|pmp:<IP>|extip:<IP>|stun:<IP:PORT> |
--nodiscover |
off | Disable discovery (manual peers only) |
--discovery.v4 / --discovery.v5 |
on | Toggle discovery protocols (aliases discv4/discv5) |
--netrestrict <CIDR> |
— | Restrict peers to given IP networks |
--discovery.dns <urls> |
network default | DNS discovery entry points ("" to disable) |
--nodekey <file> / --nodekeyhex <hex> |
auto | P2P node key |
--identity <name> |
— | Custom node name |
4.5 RPC, Engine API, and GraphQL
| Flag | Default | Purpose |
|---|---|---|
--http / --http.addr / --http.port |
off / localhost / 8545 |
HTTP-RPC server |
--http.api <list> |
net,web3 |
HTTP namespaces |
--http.corsdomain <list> |
— | Allowed CORS origins (browser enforced) |
--http.vhosts <list> |
localhost |
Allowed virtual hosts (server enforced) |
--http.rpcprefix <path> |
/ |
HTTP path prefix for JSON-RPC |
--ws / --ws.addr / --ws.port |
off / localhost / 8546 |
WebSocket-RPC server |
--ws.api <list> / --ws.origins <list> |
net,web3 / — |
WS namespaces / allowed origins |
--ipcdisable / --ipcpath <name> |
enabled / geth.ipc |
IPC server toggle / socket name |
--graphql |
off | GraphQL on the HTTP server |
--authrpc.addr / --authrpc.port |
localhost / 8551 |
Engine API listener |
--authrpc.vhosts <list> |
localhost |
Engine API virtual hosts |
--authrpc.jwtsecret <file> |
auto (<datadir>/geth/jwtsecret) |
JWT secret for the Engine API |
--rpc.gascap <n> |
50,000,000 | Gas cap for eth_call/estimateGas (0 = infinite) |
--rpc.txfeecap <ether> |
1 | Max tx fee via RPC (0 = no cap) |
--rpc.evmtimeout <dur> |
(set) | eth_call timeout (0 = infinite) |
--rpc.batch-request-limit <n> |
(set) | Max requests per batch |
--rpc.batch-response-max-size <n> |
(set) | Max batch response bytes |
--rpc.allow-unprotected-txs |
off | Allow non-EIP-155 (unprotected) txs via RPC |
Warning
--http.vhostsand--http.corsdomaindefault tolocalhost/empty for a reason. Setting either to*removes a layer of protection against DNS-rebinding and cross-origin attacks. Only widen them behind a trusted reverse proxy.
4.6 Transaction pool
| Flag | Purpose |
|---|---|
--txpool.locals <accts> |
Accounts treated as local (no flush, priority inclusion) |
--txpool.nolocals |
Disable price exemptions for local txs |
--txpool.journal <file> / --txpool.rejournal <dur> |
Local-tx journal and regeneration interval |
--txpool.pricelimit <n> |
Minimum gas-price tip for acceptance |
--txpool.pricebump <pct> |
Replacement price bump |
--txpool.accountslots / --txpool.globalslots |
Executable slot limits per account / global |
--txpool.accountqueue / --txpool.globalqueue |
Non-executable (queued) slot limits |
--txpool.lifetime <dur> |
Max time non-executable txs are queued |
--blobpool.datadir / --blobpool.datacap / --blobpool.pricebump |
Blob-transaction pool storage and sizing |
4.7 Block building (miner / payload)
| Flag | Purpose |
|---|---|
--miner.gaslimit <n> |
Target gas ceiling for built blocks |
--miner.gasprice <wei> |
Minimum gas price to include a transaction |
--miner.extradata <str> |
Block extra-data (default = client version) |
--miner.recommit <dur> |
Interval to rebuild the in-progress block |
--miner.maxblobs <n> |
Max blobs per block |
--miner.pending.feeRecipient <addr> |
Fee recipient for the pending block (not actual production) |
Note
On a post-merge network the actual fee recipient for produced blocks is supplied by the CL via the Engine API's payload attributes, not by a Geth flag. The
--miner.*flags configure block-building parameters; they do not enable PoW mining.
4.8 Metrics, telemetry, and gas-price oracle
| Flag group | Purpose |
|---|---|
--metrics, --metrics.addr, --metrics.port |
Enable and serve the metrics HTTP endpoint |
--metrics.influxdb* / --metrics.influxdbv2* |
Push metrics to InfluxDB v1 / v2 |
--rpc.telemetry* |
OpenTelemetry tracing for RPC (OTLP endpoint, auth, sample ratio) |
--gpo.blocks, --gpo.percentile, --gpo.maxprice, --gpo.ignoreprice |
Gas-price-oracle tuning |
--ethstats <url> |
Report to an ethstats service (nodename:secret@host:port) |
4.9 Fork overrides
| Flag | Purpose |
|---|---|
--override.osaka <ts> |
Override the bundled Osaka fork timestamp |
--override.bpo1 / --override.bpo2 <ts> |
Override BPO fork timestamps |
--override.ubt <ts> |
Override the UBT (binary-trie) fork timestamp |
--override.genesis <file> |
Load genesis and config from a file |
Caution
Fork-override flags are for testing and emergency coordination. Setting an incorrect fork timestamp will fork your node off the canonical chain. Do not use them on a production mainnet node unless following explicit, official guidance.
5. Deployment
5.1 Reference topology
┌────────────── host (firewalled) ──────────────┐
:30303 │ ┌───────────┐ Engine API ┌────────────┐ │
◄── P2P ──►│ │ geth │◄── :8551 + JWT ──│ CL client │ │
│ │ (EL) │ │ (beacon) │◄┼─► :9000 CL P2P
:8545 │ └─────┬─────┘ └────────────┘ │
◄── RPC ───┼────────┘ (localhost only) │
(proxy) │ jwt.hex (chmod 600) shared between the two │
└─────────────────────────────────────────────────┘
5.2 systemd unit (Geth)
# /etc/systemd/system/geth.service
[Unit]
Description=Go Ethereum (execution layer)
After=network-online.target
Wants=network-online.target
[Service]
User=geth
Group=geth
Type=simple
Restart=on-failure
RestartSec=5
TimeoutStopSec=300
ExecStart=/usr/local/bin/geth \
--mainnet \
--datadir /srv/geth \
--http --http.addr 127.0.0.1 --http.api eth,net,web3 \
--authrpc.addr localhost --authrpc.port 8551 \
--authrpc.jwtsecret /etc/ethereum/jwt.hex \
--metrics --metrics.addr 127.0.0.1
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now geth
journalctl -u geth -f
Important
Set
TimeoutStopSecgenerously (≥ 300s). Geth needs time to flush state on shutdown; a short stop timeout causes systemd toSIGKILLit, leading to a slow state-recovery rewind on the next start.
5.3 JWT secret provisioning
sudo mkdir -p /etc/ethereum
openssl rand -hex 32 | tr -d "\n" | sudo tee /etc/ethereum/jwt.hex >/dev/null
sudo chown geth:geth /etc/ethereum/jwt.hex
sudo chmod 600 /etc/ethereum/jwt.hex
Point both Geth (--authrpc.jwtsecret) and the CL client at this file.
5.4 Docker / Compose
# docker-compose.yml (excerpt)
services:
geth:
build: .
command:
- --mainnet
- --datadir=/data
- --http
- --http.addr=0.0.0.0
- --http.api=eth,net,web3
- --authrpc.addr=0.0.0.0
- --authrpc.jwtsecret=/jwt/jwt.hex
volumes:
- geth-data:/data
- ./jwt:/jwt:ro
ports:
- "127.0.0.1:8545:8545"
- "30303:30303"
- "30303:30303/udp"
volumes:
geth-data:
Warning
Inside containers you must bind
--http.addr 0.0.0.0so the port is reachable, but publish it to127.0.0.1on the host (as above) — never0.0.0.0:8545:8545on a public host.
5.5 Deployment checklist
- [ ] Chain data on NVMe/SSD with terabyte-class headroom
- [ ]
LimitNOFILE/--fdlimitraised (≥ 65536) - [ ] JWT secret generated,
chmod 600, shared with the CL client - [ ] RPC bound to
localhost(or behind a reverse proxy with auth) - [ ] Only required namespaces in
--http.api;admin/debugnot public - [ ] P2P port
30303open (TCP and UDP); NAT configured if applicable - [ ] Metrics endpoint scraped; alerts on sync status, peer count, head age
- [ ] Consensus client running and authenticated
- [ ] Graceful-shutdown timeout (
TimeoutStopSec) ≥ 300s - [ ] Backup of
keystoreandjwt.hexstored securely off-host
6. Security
6.1 Threat model and surfaces
| Surface | Risk | Mitigation |
|---|---|---|
| JSON-RPC (HTTP/WS) | Data exfiltration, tx submission, DoS | Bind localhost; restrict namespaces; vhosts/corsdomain; reverse proxy with auth/TLS |
admin/debug namespaces |
Peer manipulation, resource-heavy traces | Never expose publicly; IPC-only |
Engine API (8551) |
Unauthorized fork-choice control | JWT secret (chmod 600); bind localhost |
| Keystore | Key theft → loss of funds | File permissions; offline backup; strong passwords; hardware wallets |
| P2P | Eclipse/DoS | --netrestrict, trusted peers, adequate --maxpeers |
| Unprotected txs | Replay across chains | Keep --rpc.allow-unprotected-txs off |
6.2 Hardening rules
Caution
Apply all of the following on any internet-connected node:
- Default-deny RPC exposure. Keep
--http.addr/--ws.addronlocalhost. If remote access is required, terminate TLS and authentication at a reverse proxy; never expose Geth's raw RPC port to the internet.- Minimize namespaces. Expose only
eth,net,web3over HTTP/WS.admin,debug, andtxpoolbelong on the IPC socket.- Lock the vhost/CORS gates. Leave
--http.vhosts/--http.corsdomainat their restrictive defaults unless a proxy requires otherwise. Avoid*.- Protect the JWT secret.
chmod 600, owned by the service user. It is the credential that lets the CL drive your node.- Treat the keystore as cash. Back it up offline; the password is not stored in the file. Use
--lightkdfonly on test networks.- Run as a non-root user with a dedicated datadir and minimal filesystem permissions.
6.3 Removed legacy surface (negative knowledge)
Note
The legacy
personalRPC namespace (remote account unlock/sign over RPC) has been removed from Geth and is not available. Do not design integrations aroundpersonal_unlockAccountorpersonal_sendTransaction. Use an external signer (--signer), hardware wallets, or offline key management (ethkey) instead. This repository also does not ship theclefsigner binary or a standalonebootnodebinary — discovery/bootnode duties are covered by thedevp2ptool.
6.4 Malicious-input resistance
Geth includes an extensive fuzzing suite (tests/fuzzers: bls12381, bn256, rangeproof, secp256k1, txfetcher, difficulty) and documented post-mortems (docs/postmortems) and security audits (docs/audits). Consensus-critical code paths (EVM, trie, crypto, RLP) are the primary fuzz targets. Operators should track upstream security advisories and apply patch releases promptly, as a consensus bug can fork a node off the network.
7. Operations
7.1 Start, stop, status
sudo systemctl start geth
sudo systemctl stop geth # graceful; allow up to TimeoutStopSec
sudo systemctl status geth
journalctl -u geth -f # follow logs
Health check from a console:
geth attach --exec 'eth.syncing' # false when synced
geth attach --exec 'net.peerCount' # healthy peer count
geth attach --exec 'eth.blockNumber' # head should advance ~every 12s
7.2 Logging
Verbosity is controlled by the debug flag group: --verbosity <0-5> (0=silent, 3=info default, 5=trace). --log.format, --log.file, and pprof flags are available for structured output and profiling. The internal/debug package also exposes a runtime debug RPC namespace and an embedded pprof server.
7.3 Backup and restore
| Asset | Backup method | Notes |
|---|---|---|
| Keystore | Copy <datadir>/keystore |
Portable; passwords stored separately |
| JWT secret | Copy /etc/ethereum/jwt.hex |
Regenerate + restart both clients if rotated |
| Chain data | Snapshot the volume while Geth is stopped | Or simply re-sync; chain data is reproducible |
| Era archives | Copy --datadir.era / export with export-history |
Verifiable against accumulator roots |
Tip
Chain and state data are fully reproducible from the network, so the only irreplaceable assets are the keystore and (for coordination) the JWT secret. Prioritize those in backup policy; treat chain data as a cache that can be rebuilt by re-syncing.
7.4 Database maintenance
# Stop geth first for most of these
geth snapshot verify-state # integrity check of state vs. snapshot
geth db check-state-content # verify state is cryptographically correct
geth db metadata # inspect database metadata
geth prune-history # reclaim space from old bodies/receipts
geth removedb # nuke chain/state (keeps keystore) → re-sync
Warning
geth db delete/geth db putperform raw key mutations and can corrupt the database — the help text says so explicitly. Use them only when you understand the exact key schema incore/rawdb.
7.5 Upgrades
sudo systemctl stop geth
# build/install the new geth binary
geth version # confirm the new build
sudo systemctl start geth
journalctl -u geth -f # watch for migration/recovery messages
Important
Read the release notes before upgrading across a network fork. Some releases change the state scheme, require a database migration, or carry a hard-fork activation. Upgrade the CL client and Geth in the order the release notes specify, and never skip a fork-activating release on mainnet.
7.6 Incident playbook
| Symptom | First checks | Action |
|---|---|---|
| Head not advancing | Is the CL running and authenticated? eth.syncing? |
Restart CL; verify JWT; check Engine API logs |
| Falling behind | Disk type/IO, peer count, CPU | Move to NVMe; raise --cache; check peers |
| Crash-loop on start | State recovery / corrupt DB messages | Allow recovery; if corrupt, removedb + re-sync |
| Out of disk | df; --datadir.minfreedisk triggered |
Free/extend volume; enable history pruning |
| No peers | Firewall, NAT, bootnodes | Open 30303 TCP+UDP; set --nat; check --bootnodes |
| RPC unreachable | Was --http set? Right addr/vhosts? |
Enable transport; check bind address and namespaces |
8. Repository Structure (Developer Reference)
The codebase is organized by subsystem. Top-level packages and their roles:
| Path | Role |
|---|---|
cmd/geth |
The geth binary: CLI app, subcommands, node assembly |
cmd/utils |
Shared flag definitions and node/config builders (the config surface) |
cmd/{abigen,evm,devp2p,ethkey,era,rlpdump,abidump,blsync,workload,fetchpayload} |
Companion tools |
node |
Service container, RPC stack, lifecycle |
eth |
The execution-layer service; eth/ethconfig holds its config |
eth/downloader |
Snap/full sync engine |
eth/protocols/{eth,snap} |
Wire protocols |
eth/fetcher |
Block/transaction fetching |
eth/filters |
Log/event filtering (eth_getLogs, subscriptions) |
eth/gasprice |
Gas-price oracle |
eth/tracers |
Native + JS transaction tracers |
eth/catalyst |
Engine API implementation |
core |
Blockchain, genesis, state processor |
core/vm |
The EVM |
core/state |
StateDB and state objects |
core/txpool (legacypool, blobpool) |
Transaction mempool |
core/rawdb |
Low-level DB schema + freezer |
core/filtermaps |
Log index |
core/tracing |
Tracing hooks |
core/forkid |
Fork identity |
consensus/{beacon,clique,ethash,misc} |
Consensus engines and fork rules |
trie, trie/bintrie, trie/trienode |
State trie (MPT + experimental binary trie) |
triedb, triedb/{pathdb,hashdb} |
Trie node store schemes |
ethdb (pebble, leveldb, memorydb, remotedb) |
KV database abstraction |
p2p (discover, enode, enr, rlpx, dnsdisc, nat, netutil) |
Networking stack |
accounts (keystore, usbwallet, scwallet, abi) |
Account management |
signer (fourbyte, core, storage) |
Signing helpers |
rpc |
JSON-RPC framework |
internal/ethapi |
RPC method implementations |
graphql |
GraphQL API |
miner |
Block/payload building |
beacon (light, blsync, engine, params, merkle) |
Beacon light client + Engine types |
metrics, log, params, rlp, common |
Cross-cutting infrastructure |
ethclient |
Go client library |
internal/era, core/rawdb/eradb |
Era archive format |
ethstats |
ethstats reporting |
8.1 Building and testing
make geth # build the geth binary
make all # build all binaries
go test ./... # run the full test suite
go test ./core/... # subsystem-scoped tests
The repository uses code generation (go:generate with gencodec, stringer, protoc-gen-go; see the tool directive in go.mod) and rlp/rlpgen for RLP codecs. Regenerate generated code rather than hand-editing it.
8.2 Contributing
Contributions follow upstream Go conventions: gofmt-clean code, focused commits, and full test coverage for consensus-critical changes. Consensus-affecting code (EVM, trie, crypto, RLP, fork rules) carries the highest review bar because a divergence forks nodes off the network. The fuzzing harnesses in tests/fuzzers should be extended alongside changes to parsing or cryptographic code paths.
9. Internals Deep Dive: Request and Block Lifecycles
9.1 Inbound block (from a peer)
- A peer announces a block via the
ethprotocol;eth/fetcherrequests the body. core.BlockChainvalidates the header (consensus engine) and re-executes transactions throughcore/vm, mutating acore/state.StateDB.- The resulting state trie is committed via
trie→triedb→ethdb; immutable old data eventually migrates to the freezer. - If the block extends the canonical chain (per CL fork choice), the head advances; otherwise a reorg is evaluated.
9.2 Engine API payload (from the CL)
- The CL calls
engine_forkchoiceUpdatedwith payload attributes (incl. fee recipient) on the authenticated port. eth/catalystinstructsminerto build a payload: pull transactions fromcore/txpool, execute them, and assemble a block.- The CL retrieves the payload, and after consensus, calls
engine_newPayloadto commit it; Geth validates and imports it as in §9.1.
9.3 JSON-RPC call (eth_call)
- The
rpcserver decodes the request and routes it to theethnamespace handler ininternal/ethapi. - The handler snapshots state at the requested block, runs the call through
core/vmunder the--rpc.gascapand--rpc.evmtimeoutlimits, and returns the result without mutating state.
9.4 Sync (snap)
eth/downloaderselects a pivot and requests account/storage ranges viaeth/protocols/snap.- State is reconstructed into
triedb; block bodies are backfilled. - Once the pivot is reached, the node switches to full validation and follows the head under CL fork choice.
10. Improvements and Technical Notes
Observations relevant to operators and maintainers, derived from the source structure:
- State-scheme migration friction. Switching between
pathandhashschemes (orfull↔archive) is not a live toggle and generally requires a re-sync. Operators should choose deliberately at first sync; documentation and tooling that make the migration path clearer would reduce a common operational pitfall. - Binary trie is in flight.
trie/bintrie, thegeth bintrie convertcommand, and the--override.ubtfork flag indicate an in-progress migration toward a binary state trie. Treatbintriefeatures as experimental and not for production state. - Two-client coordination is the dominant failure mode. The majority of "node won't sync" situations reduce to a missing/misauthenticated CL client. Surfacing Engine-API connectivity prominently in health output and logs continues to pay off.
- Large modules.
triedb/pathdb,core,core/rawdb,trie, andcore/stateare the largest and most consensus-critical modules; changes there warrant the deepest review and fuzzing. - Open upstream issues to track (from the community issue tracker at analysis time): a reported freezer error (
canonical hash missing, can't freeze block) and an external-signer EIP-7702 authorization-list handling issue. Operators relying on freezer pruning or external signers with set-code (EIP-7702) transactions should monitor these. - Config reproducibility. The
dumpconfig/--configTOML round-trip plusGETH_*env vars make Geth well-suited to declarative configuration management; standardizing on a committedgeth.tomlreduces drift across a fleet.
11. Quick Reference
11.1 Default ports
| Service | Port | Protocol |
|---|---|---|
| P2P (discovery + transport) | 30303 |
TCP + UDP |
| HTTP-RPC | 8545 |
TCP |
| WebSocket-RPC | 8546 |
TCP |
| Engine API (auth) | 8551 |
TCP |
| Metrics (when enabled) | 6060 |
TCP |
11.2 Default RPC modules
| Endpoint | Default modules |
|---|---|
| HTTP | net,web3 |
| WebSocket | net,web3 |
| Auth (Engine API) | eth,engine |
| IPC / console | all local namespaces |
11.3 Essential operations
| Task | Command |
|---|---|
| Build | make geth |
| Dump config | geth dumpconfig --mainnet > geth.toml |
| Attach console | geth attach |
| Check sync | geth attach --exec 'eth.syncing' |
| Reset DB (keep keys) | geth removedb |
| Verify state | geth snapshot verify-state |
| Export/import chain | geth export <file> / geth import <file> |
| Init custom genesis | geth --datadir <dir> init genesis.json |
11.4 Key file locations
| Path | Contents |
|---|---|
<datadir>/keystore |
Encrypted account keys |
<datadir>/geth/chaindata |
Chain and state database |
<datadir>/geth/jwtsecret |
Auto-generated Engine API JWT (if not overridden) |
<datadir>/geth.ipc |
IPC socket |
--datadir.ancient |
Freezer (frozen old blocks) |
--datadir.era |
Era history archives |