Aller au contenu

CLI flags

Every flag is also printable with:

umnode --help

--help groups the flags by purpose (General, Networking & discovery, Chain sync, Block store, Execution & state, Segment archive, JSON-RPC, Observability); the reference table below is a flat list.

Flags marked ⚙ can also be set per-network in the config YAML; an explicit flag overrides the config value (see settings precedence).

Boolean flags: spaced or =

Boolean flags accept both --backfill=false and --backfill false — the two forms are equivalent, as they are for value flags. (Under the hood a boolean flag won't natively consume the next token, so the node folds --flag value into --flag=value before parsing.) A bare --backfill still means true. The node takes no positional arguments, so a stray non-flag token is rejected rather than silently ignored.

All flags

Flag Default Meaning
--config cfg/ethereum.yaml Path to the network config YAML.
--network mainnet Network name from the config file. The bundled file has mainnet, sepolia, hoodi and ephemery (see Bundled networks).
--discovery-addr 0.0.0.0:30303 UDP bind address for peer discovery. UMNode never listens for inbound connections.
--max-peers ⚙ 30 Maximum number of peers to connect to.
--log-level info Log level: trace, debug, info, warn, error, crit.
--log-p2p false Include the p2p subsystem's debug logs (peer/discovery/wire) at --log-level debug. p2p info and above always show; --log-level trace shows everything regardless.
--discovery-v4 ⚙ true Enable discovery v4.
--discovery-v5 ⚙ false Enable discovery v5.
--no-discovery ⚙ false Disable discovery entirely — the DHT and DNS discovery; rely on static/trusted nodes only.
--dns-discovery true Also discover peers from the network's EIP-1459 DNS node lists: the config's DNSDiscovery, else go-ethereum's known tree for mainnet, Sepolia and Hoodi. Keeps finding peers after the bootnodes are gone. Off under --no-discovery.
--drop-faulty-peers ⚙ true Disconnect a peer after it repeatedly serves undecodable or invalid (failing the header roots) responses, so one broken peer can't stall the fetcher. =false keeps such peers.
--require-fork-match ⚙ false Reject a peer at the handshake unless its fork hash matches ours. Off by default — a read-only node benefits from staying connected to compatible-but-different peers.
--require-served-range ⚙ false Only request a block from an eth/69+ peer whose advertised served range covers it (eth/68 peers, which advertise no range, are still used). Off by default.
--storage-report-every ⚙ 10s Interval for the Block store sealedSegments=… sealedBlocks=… sealedLast=… openBlocks=… openLast=… tailBlocks=… tailLast=… tier summary; needs --block-db (0 disables). The *Last fields are the highest block number in each tier.
--head-poll-every ⚙ 4s How often to poll a peer for the chain head (0 disables).
--node-db data/p2p/enode Directory for the persistent (discovery) node database.
--inventory-db data/p2p/inventory Directory for the observed-peer inventory (empty disables persistence).
--block-db data/chain Directory for the segmented block store (empty disables block persistence).
--realtime ⚙ true Run the realtime worker that writes upcoming blocks to the store; needs --head-poll-every > 0 and --block-db.
--backfill ⚙ true Run the backfill worker that fills chain history; needs --block-db.
--backfill-forward-direction ⚙ true Fill history forward (genesis-first, in-order — lets an indexer consume blocks during import) or, =false, reverse (head-down, legacy). See Backfill direction.
--backfill-report-every ⚙ 10s How often to log backfill progress (fill %, import rate); 0 disables. See Progress logging.
--backfill-from 0 Lower bound of the fill range, inclusive (0 = genesis).
--backfill-to 0 Upper bound, inclusive (0 = auto: the chain head for forward; for reverse, realtime's earliest written block, or the head on an empty store).
--request-timeout ⚙ 15s Per-peer timeout for each header/body/receipt request.
--init-start-block 0 Initialize a fresh block store at this block instead of the live head. Applies only when the database has no blocks yet; ignored once it does (or when 0).
--max-catch-up ⚙ 4096 Max blocks filled per catch-up chunk; the worker loops over chunks until it reaches the head.
--fetch-batch ⚙ 128 Blocks per header/body/receipt request batch.
--fetch-concurrency ⚙ 16 Max parallel fetch batches; actual parallelism scales to peer count up to this cap.
--fetch-retries ⚙ 3 Fetch attempts per batch before giving up.
--rpc-peers ⚙ (none) JSON-RPC endpoints of existing nodes on this network — synced or partially synced — to fetch block data from, comma-separated or repeated: http(s):// or ws(s):// URLs, or IPC socket paths. They are tried before p2p peers for headers, bodies and receipts and feed the head tracker; each must serve this network's genesis block. Whatever an endpoint lacks (blocks above a partially synced node's head, pruned history) falls through to the other sources. A network with no p2p nodes configured runs from its RPC peers alone. See RPC peers.
--rpc-peer-concurrency ⚙ 4 Requests kept in flight per RPC peer. The fetcher counts an endpoint as this many peers when sizing parallel batches and hands the overflow to p2p peers.
--rpc-peer-batch ⚙ 16 Blocks per bodies/receipts request to an RPC peer — one JSON-RPC batch call; headers go 8× that per call. Lower it for endpoints with small batch or response-size limits.
--segment-size ⚙ 50000 Blocks per segment. Locked at database creation.
--block-group-size ⚙ 128 Blocks compressed together as one unit in sealed segments (1 = per-block). Larger shrinks the store, but a random single-block read inflates the whole group (sequential range scans are unaffected); 64–128 is the sweet spot. Locked at database creation. See Performance.
--compression ⚙ s2 Segment compression codec: s2 (default — Snappy's faster, denser successor; decodes at memory speed whatever the level; tiers via --compression-level / --seal-compression-level), zstd (smallest on disk, slower to encode and decode) or snappy (no levels). Not locked: every segment header names its codec, so a change applies to segments written from now on. See Compression codecs.
--compression-level ⚙ 1 Compression level for block import only — grouped segments are re-packed at --seal-compression-level, so this does not change final size, only import speed (lower = faster). The level picks an encoder tier: s2 has 3 (1-2 fastest, 3-9 better, 10+ best), zstd 4 (1-2 fastest, 3-5 default, 6-9 better, 10+ best); ignored for snappy. See Performance.
--seal-compression-level ⚙ 6 Compression level for the off-hot-path block-grouping re-pack at seal — sets the final on-disk size of sealed segments (0 = inherit --compression-level). Same tiers as --compression-level: 6-9 = better (the default), 10+ = best (so 10 == 19). Higher = smaller with no read penalty, only more background CPU — and under s2 the best tier is not worth it: it re-packed a 50k-block BNB segment in 3.5 to 9 minutes where better took 38 s for the same size. See Performance.
--repack-segments false Maintenance. Rewrite every sealed block segment — and, with --execute, every sealed changeset and receipts segment under --state-db — whose codec differs from the configured --compression / --state-changeset-compression, in that codec at the seal tier, swapping each in by rename; then exit. Makes a store that lived through a codec switch uniform; interruptible and re-runnable. Run it with the node stopped.
--pending-db ⚙ pebble Pending-area key-value engine: pebble or leveldb. Locked at database creation.
--seal-confirmations ⚙ 256 Blocks the tip must advance past a full segment before it is sealed.
--block-history all Block retention: all keeps every block (archive); a number N keeps only the most recent N blocks — whole sealed segments below the floor are deleted, so the effective tail is at least the open segment plus --seal-confirmations. The master retention: --state-history and --receipts-history are capped to N. Pruning never passes the executor: with --execute, blocks not yet executed stay until execution catches up; nor the segment archive: with --backup, a sealed segment not backed up yet stays until it is. Pruned blocks read as absent and a state rebuild from genesis becomes impossible; raising N later (or all) fetches the pruned history back, whole segments from the archive and the rest from peers. See Pruning.
--extra-archive-blocks (none) Block ranges kept outside the --block-history window, as A:B pairs separated by commas (1000000:1500000,3000000:3100000): their sealed segments are never pruned and, where missing, are fetched once the window is filled — whole segments from the archive, the rest from peers — each range padded 256 blocks below (what BLOCKHASH and the Parlia rules look back at) and widened to whole segments. With --execute the state history is kept continuous from the block before the lowest range up — its first block executes on that state, and a read needs every change above it — and fetched back from the archive where it was pruned, so eth_call and traces serve inside the ranges when the archive has the history; the ranges' receipts stay, or come back from the archive. Without an archive the ranges hold blocks only. Named on each start, like the window: a start without the flag treats the ranges as pruned history. No effect with --block-history all.
--seal-progress-every ⚙ 30s How often the background block-grouping re-pack logs progress (0 silences it). The re-pack always runs off the store lock, so reads/writes are never blocked.
--compaction-interval ⚙ 1h How often to compact the pending block area and peer inventory to reclaim space (0 disables).
--index ⚙ true Build the block-hash / tx-hash index behind eth_getBlockByHash and eth_getTransactionByHash; =false skips the index DB. See Hash indexing.
--index-key-bytes ⚙ 8 Leading bytes of each hash kept in an index key (4–32; 32 = the whole hash). Fewer = smaller index, more truncated-key collisions (always resolved by re-checking the full hash). Locked at index creation.
--reindex-report-every ⚙ 30s How often the background hash-index reindexer logs progress (fill %, rate, ETA); 0 disables. See Hash indexing.
--execute true Build state by executing every stored block from genesis through the EVM (execution subsystem), serving balances, eth_call and callTracer traces. Needs --block-db, a genesis definition and a backfill that reaches genesis; until those hold, state RPC reports not ready. =false skips state building entirely. When prerequisites are missing and the flag was not set explicitly, execution turns itself off with a warning instead of failing startup.
--execute-from 0 Debug. Rebuild the state from this block at startup. With an existing state, everything from this block up is cut — unwound through the changesets, sealed history included — and re-executed; with no state yet, the genesis allocation is seeded as the post-state of the block before it: a deliberately wrong state, executed unverified from then on (senders get their nonce set and their balance topped up so every transaction runs; nothing is checked against the headers) for performance research. The unverified mark sticks to that state store until it is rebuilt from genesis. 0 (the default) builds from genesis as usual. Takes effect on every start it is passed. See Debugging a block range.
--execute-to 0 Debug. Do not execute past this block: the state parks at it (RPC keeps serving it) until the node restarts without the flag, when execution resumes from the stored head as usual. 0 (the default) follows the block store's head.
--execute-cpu (empty) Reserve a CPU for the execution thread. auto pins it to the fastest core (highest cpuinfo_max_freq; its hyperthread siblings are kept idle too) and keeps every other thread of the node — compaction, sealing, GC workers — off that core; a CPU list (8 or 8,9) pins and reserves exactly those CPUs; empty (the default) leaves the thread unpinned. Execution is one serial loop, so a core it never shares is worth more to it than any number of shared ones. Linux only.
--state-db data/state Directory for the executed-state store (flat state, changesets, history index); used with --execute.
--state-history all State history retention: all keeps every block traceable (archive); a number N keeps changesets for only the most recent N blocks — reads below the floor return a state history not available error; raising N later (or all) fetches the pruned changesets back from the archive. Keep N ≥ --seal-confirmations so reorg unwinds never hit the pruned floor.
--receipts derive Where receipts come from. derive (the default) skips the download: the executor produces every block's receipts and keeps them in a prunable receipts stream under <state-db>/receipts (pre-Byzantium blocks, whose receipts carry a state root execution cannot recompute, are still fetched); without --execute nothing derives them, so receipts are not available until execution is on or the mode is switched to fetch. fetch downloads them with each block and stores them inline — and backfills them for blocks an earlier run stored without them (see Receipts and logs). none neither downloads nor stores receipts — the lightest import (with --execute, point lookups still re-execute on request). auto, the former default, follows --execute: derive when it is on, else fetch.
--receipts-history all Receipts retention with --receipts derive: all keeps every block's receipts; a number N keeps only the most recent N blocks. Below the floor eth_getTransactionReceipt / eth_getBlockReceipts re-derive by executing the block; eth_getLogs returns a receipts not available error. Raising N later (or all) fetches the pruned receipts back from the archive.
--autotune true Size the state store and execution caches for this host: about 60% of its RAM (or the container's memory limit) is budgeted across the Pebble block cache, the memtables, the execution read caches and a Go heap limit (GOMEMLIMIT, unless the environment sets one), and the compaction concurrency follows the core count. Only the knobs left at 0 are sized; an explicit value is kept and charged against the budget. =false keeps the static defaults. See Sizing for the host.
--state-db-cache 0 Pebble block cache for the state store, in MB, shared by the flat and history instances. 0 sizes it for the host (about a quarter of the --autotune budget), or 512 with --autotune=false. Pebble's own default (8 MB) is far too small for state lookups.
--state-db-compaction 0 Max concurrent compactions per state-store instance; 0 auto-selects half the cores (min 2). Pebble's own default (1) cannot keep up with the archive write load and stalls execution as the store grows.
--state-db-memtable 0 Size of one state-store memtable, in MB, before it is flushed; 0 sizes it for the host (64/128/256 for hosts under 8 GB, under 16 GB, and above), or 256 with --autotune=false. Larger memtables make flushes fewer and bigger; the footprint is roughly --state-db-memtable-stop × this × 2 instances.
--state-db-memtable-stop 0 Number of queued state-store memtables allowed before writes stall waiting for a flush; 0 sizes it for the host (2–8, from the memtables' share of the --autotune budget), or 2 with --autotune=false. Raising it lets execution keep running while a slow disk drains a flush, at up to memtable-stop × memtable MB of RAM per instance. See write-stall headroom.
--state-db-l0-stop 0 L0 file count that stalls state-store writes until compaction reduces it; 0 selects the default (24). Raising it tolerates a deeper L0 backlog (more read amplification) before stalling, buying compaction time on a disk that briefly falls behind.
--state-db-l0-compact 0 L0 file count that triggers an L0→Lbase compaction; 0 selects the default (4). Pebble's stock 500 lets the history instance's ascending-key changesets pile up ~500 non-overlapping L0 files (sublevels stay 1, so the read-amplification trigger never fires) before one giant compaction, growing compaction debt unbounded. A low value keeps L0 small with cheap, near-trivial file moves. See history-store L0 backlog.
--state-db-lbase 0 Size of the state-store base level (Lbase), in MB; 0 selects the default (256). Larger flattens the LSM as the archive grows.
--state-db-zstd false Compress the history instance's SSTable blocks with zstd instead of the default Snappy-framed S2 (smaller on disk). Off by default: in a cgo-less build Pebble creates a new zstd encoder for every block it writes and a new decoder for every block it reads, which on a busy history store allocates tens of GB/s and keeps the garbage collector running continuously — it was the dominant CPU cost of catch-up. Only worth it in a cgo build (Pebble then uses the C zstd).
--state-db-no-wal false Disable the state store's write-ahead log to roughly halve write volume during a replayable catch-up/backfill. State becomes durable only at periodic checkpoints; a crash re-executes everything since the last checkpoint from the block store. Incompatible with the realtime worker (--realtime); only for runs you can rebuild from blocks. See Disabling the WAL.
--state-debug-no-history false Debug. Commit the flat state only: no changeset blobs, no history-index appends, no changeset pre-value reads. The store cannot be unwound or read at a historical block, so this is for throwaway performance-research runs that measure what the history write path costs (see Where execution time goes).
--state-db-checkpoint 0 With --state-db-no-wal, the per-instance write volume (MB) that forces a coordinated checkpoint; 0 selects the default (¾ of the memtable size). Must stay below --state-db-memtable so a checkpoint flushes before Pebble auto-flushes a full memtable.
--state-db-hist-cache 0 Max number of per-key live history shards cached in memory; 0 sizes it for the host (200000–2000000 by heap budget), or 200000 with --autotune=false. Spares the commit path a per-key read of the still-filling shard on the next block that touches the key; larger holds more hot keys at more memory.
--state-changeset-segments false Deprecated / no-op. As of schema v3, per-block changesets are always stored in the segment store under <state-db>/changesets (the history Pebble instance is index-only). The flag is accepted for compatibility and ignored. See State history as segments.
--state-execute-batch 0 Number of executed blocks to accumulate before one combined state-store commit during catch-up; 0 pairs it with the memtable (one block per memtable MB, 64–256), or 256 with --autotune=false. Larger batches amortize per-commit cost and coalesce more per-key history appends, at more memory held before each commit; at the tip the batch always flushes per block, so RPC freshness is unaffected. Keep it below what fits in one --state-db-memtable when --state-db-no-wal is set.
--state-cache-accounts 0 Max accounts in the cross-block execution read cache — the first-line account/slot/code reader, in front of --state-db-cache; 0 sizes it for the host (about 30% of the Go heap budget, two slots per account), or 300000 with --autotune=false. Enlarging it on a memory-rich host cuts the repeated point-misses that dominate read-heavy ranges.
--state-cache-slots 0 Max storage slots in the cross-block execution read cache; 0 sizes it for the host (twice --state-cache-accounts), or 600000 with --autotune=false.
--state-cache-code 0 Byte budget, in MB, for contract code in the cross-block execution read cache; 0 sizes it for the host (32–512 by heap budget), or 64 with --autotune=false.
--state-changeset-segment-size 0 Blocks per sealed changeset segment in the changeset store (<state-db>/changesets); 0 selects the default (50000). Locked at creation. Larger seals less often (fewer, bigger files); smaller makes detach/prune finer-grained.
--state-changeset-group-size 0 Changeset blocks compressed together as one group in a sealed segment; 0 selects the default (128). Locked at creation. A point-in-time read decompresses one whole group (memoized per request, so adjacent blocks are read once), so larger shrinks the store but inflates a single cold historical read; 64–256 is the sensible range.
--state-changeset-compression s2 Codec sealed changeset and receipts segments (<state-db>/changesets, <state-db>/receipts) are written with: s2 (default) or zstd (smaller, slower). Not locked: applies to segments sealed from now on; earlier segments name their own codec and stay readable.
--state-changeset-compression-level 0 Compression level (encoder tier) sealed changeset and receipts segments are compressed at; 0 selects the default (6, the better tier — like --seal-compression-level). Same tiers as the block store (s2: 3, zstd: 4). Not locked: applies to segments sealed from now on. A seal packs one whole segment at a time, so a higher level costs CPU once per segment, not per block.
--state-changeset-seal-confirmations 0 Blocks the head must advance past a full changeset segment before it seals (keeping recent changesets rewritable across reorgs); 0 selects the default (256). Must be ≥ the reorg depth — keep it aligned with --seal-confirmations.
--rpc-addr 0.0.0.0:8550 host:port for the JSON-RPC HTTP server (empty disables RPC).
--rpc-compression true gzip JSON-RPC responses when the client advertises gzip.
--rpc-gas-cap 50000000 Gas ceiling for eth_call; a call's gas defaults to (and is capped at) this.
--rpc-call-timeout 5s Execution deadline for eth_call; longer-running calls are aborted.
--rpc-trace-timeout 10s Per-transaction execution deadline for the debug_trace* methods.
--rpc-logs-max-blocks 10000 Widest block range one eth_getLogs query may scan; wider ranges are rejected so clients page instead of pinning a core.
--metrics-enabled true Serve Prometheus metrics. Disable with --metrics-enabled=false.
--metrics-addr 0.0.0.0:18126 host:port for the Prometheus /metrics endpoint.
--pprof-addr (none) host:port to serve Go's net/http/pprof profiling endpoints under /debug/pprof/ (CPU, heap, goroutine, block, mutex); empty disables them. Bind it to localhost. See Profiling.
--syncmode ⚙ snap How an empty node gets its state. snap restores the segment archive's newest state snapshot — at or below --backfill-to, when set — into the empty --state-db and executes on from its head, downloading only the blocks the node keeps (--block-history) plus those from the snapshot's head up; full builds the state from genesis, which needs every block. Snap falls back to full, with a warning, when the archive is unreachable or publishes no snapshot for the network; a node whose state store already exists continues as it is either way. A snapshot is the publisher's own derived state: the executor's parent-hash check refuses to continue on one whose head does not match the chain, the restore refuses one of another network or genesis and verifies the published digest, but the state itself is trusted — use an archive you trust.
--archive-source ⚙ (public archive) The segment archive to download from — sealed block segments the backfill attaches instead of fetching their blocks from peers (every one verified against the chain the store holds; an archive sealed in another layout is converted to the local one), and the state snapshot under --syncmode snap: an http(s)://, s3://, r2:// or file:// root URL, used exactly as given. Empty selects the public archive at https://umn.zhurko.de under the path derived from the network; none uses no archive. Needs --block-db and, for the segments, the backfill worker.
--backup ⚙ (none) Back up every sealed segment — blocks, and with --execute the state history and receipts stream — and the state snapshots to this segment archive as they seal: s3://bucket[/prefix] (credentials from AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY), r2://bucket[/prefix] (Cloudflare R2; credentials from R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY) or file:///dir. Only data files go up, listed in the archive's manifest; indexes are rebuilt by whoever downloads. Pruning never drops a sealed segment the archive does not hold yet. The URL is the archive root, used exactly as given; one archive holds one chain. Needs --block-db. (--archive-upload, the former name, is still accepted.)
--state-snapshot-every 0 With --backup and --execute, also publish a snapshot of the executed state — as state/<head>.tar.zst — once the executed head reaches N blocks and then every N blocks, so a fresh node can restore one (--syncmode snap) and execute on from its head instead of from genesis. 0 schedules none; a snapshot is taken on request (debug_snapshotState) either way, and debug_cancelStateSnapshot interrupts one. Taking one pauses commits for a flush and a hard-linked checkpoint; the pack and upload run off the executor. (Formerly --archive-state-every.)
--state-snapshot-keep 2 How many state snapshots stay published; older ones are deleted from the archive as new ones land. (Formerly --archive-state-keep.)
--state-snapshot-digest true Compute the state digest when publishing a snapshot (a full scan of the flat state, off the executor) and verify it when restoring one; =false skips both. (Formerly --archive-state-digest.)
--s3-endpoint ⚙ (AWS) Endpoint URL of an S3-compatible service (MinIO, B2, …) for s3:// archives; also read from AWS_ENDPOINT_URL.
--s3-region ⚙ (env, else us-east-1) Signing region for s3:// archives (AWS: the bucket's region); also read from AWS_REGION / AWS_DEFAULT_REGION.
--s3-path-style ⚙ auto Address S3 objects as <endpoint>/<bucket>/<key> instead of <bucket>.<endpoint>/<key>; on by default with --s3-endpoint, off for AWS.
--r2-account-id ⚙ (env) Cloudflare account id for r2:// archives; selects the endpoint https://<account id>.r2.cloudflarestorage.com. Also read from R2_ACCOUNT_ID / CLOUDFLARE_ACCOUNT_ID.
--r2-jurisdiction ⚙ (none) eu or fedramp for r2:// buckets created in that jurisdiction; also read from R2_JURISDICTION.

Notes on specific flags

Discovery & networking

  • --discovery-addr is a UDP bind for the discovery protocol only. UMNode does not open a listening TCP socket — it dials out exclusively.
  • --no-discovery turns off the DHT; combine with StaticNodes/TrustedNodes in the config to reach a private or permissioned network.

Persistence

  • --node-db is geth's discovery node database (endpoints, ping/pong times).
  • --inventory-db is UMNode's own record of peers that completed the eth handshake on the right network/fork — the set it re-dials after a restart. Set it to empty to disable persistence.
  • --block-db is the segmented block store. Empty disables block persistence entirely.
  • --compaction-interval periodically compacts the two persistent key-value databases — the pending block area and the peer inventory — to reclaim space from deleted blocks (dropped when a segment seals) and overwritten peer records (rewritten on every reconnect). Both engines compact online, so reads and writes continue throughout; 0 disables it and relies on the engine's own background compaction.

  • The discovery and inventory databases live under data/p2p/ so all P2P state sits in one directory, separate from the block store under data/chain.

Realtime sync

  • --realtime runs the worker that writes upcoming blocks (header, body and receipts) to the store as the head advances. It needs both --block-db and --head-poll-every > 0 (its source of new heads); with polling off it logs a warning and stays idle.
  • --request-timeout bounds each individual header/body/receipt request to a peer, so one unresponsive peer can't stall a fetch — the request is retried on another peer. See Realtime sync.
  • --init-start-block sets the block a fresh store initializes at instead of the live head — useful to begin syncing from a chosen point, or to reproduce a deep catch-up. It only applies when the database has no blocks yet: once the store is initialized it is ignored, and a value above the current head clamps to the head. Pick a multiple of --segment-size if you want the anchored segment to seal during the run — a mid-segment anchor leaves that one segment unsealed until backfill fills below it. See Realtime sync.
  • --max-catch-up, --fetch-batch, --fetch-concurrency and --fetch-retries tune catch-up throughput: the worker fills --max-catch-up blocks per chunk and loops over chunks until it reaches the head, while the fetcher pulls --fetch-batch-sized batches in parallel — as many at once as there are eth/69+ peers, up to --fetch-concurrency. See Realtime sync.

Backfill sync

  • --backfill runs the worker that fills chain history. It needs --block-db; with no store it has nowhere to write and does not start. It reuses the same fetcher flags as realtime and --max-catch-up for its chunk size. See Backfill sync.
  • --backfill-forward-direction (default true) fills forward — genesis-up, in ascending order — so an indexer/state-builder can consume blocks in order as they import. =false fills reverse (head-down, the legacy behaviour). Note: while realtime runs, forward builds a separate genesis-up range, so the store is not contiguous from genesis to the head until backfill reaches it. See Direction.
  • --backfill-report-every (default 10s, 0 disables) controls how often the worker logs a progress line — fill percentage plus the sustained import rate (blocks/sec) — like --storage-report-every does for the block store. See Progress logging.
  • --backfill-from / --backfill-to bound the fill range, inclusive. from (default genesis) is where forward starts / reverse stops. to (default auto = the chain head for forward; for reverse, realtime's earliest written block, or the head on an empty store) caps the top. The node refuses to start if from exceeds a set to. See Limiting the range.

RPC peers

  • --rpc-peers adds JSON-RPC endpoints of existing nodes — a synced archive, a node still syncing, another UMNode — as block sources. The fetcher tries them before the p2p peers for every header, body and receipts request, and validates what they return against the headers exactly as it does for wire peers, so an endpoint is trusted for nothing. Each endpoint must serve this network's genesis block (one on another chain is rejected for good), is probed every --head-poll-every for its latest block — which also feeds the head tracker — and rests with backoff when it fails. Whatever an endpoint lacks — blocks above a partially synced node's head, pruned history, a missing eth_getBlockReceipts — falls through to the other peers. See RPC peers.
  • --rpc-peer-concurrency bounds the requests in flight per endpoint; the fetcher counts an endpoint as that many peers and hands the overflow of a wide fetch to the p2p peers. --rpc-peer-batch is the blocks per bodies/receipts request (one JSON-RPC batch call); lower it for providers with small batch or response-size limits.
  • A network with --rpc-peers and no p2p nodes at all (none in the config, --no-discovery) runs from its RPC peers alone: they supply the head and every block. debug_listPeers shows each endpoint's state.

Block store (some settings are permanent)

--segment-size, --block-group-size and --pending-db are written into the store's MANIFEST the first time the database is created and are locked — UMNode refuses to start if a later run requests different values, because they are baked into every on-disk path, byte and engine file. --compression (the codec), --compression-level (block import) and --seal-compression-level (final sealed size) are not locked: every segment header names its codec, so a change applies to segments written from now on. A level picks an encoder tier — s2 has 3 (1-2 fastest, 3-9 better, 10+ best), zstd 4 (1-2 fastest, 3-5 default, 6-9 better, 10+ best) — so many numbers are equivalent, e.g. 10 and 19 produce identical bytes. The startup Block store ready log prints the resolved tier, e.g. compressionLevel="1 (fastest)" sealCompressionLevel="6 (better)". See Performance for how to tune these and --block-group-size.

--seal-confirmations is a runtime policy, not locked: a full segment is sealed into its immutable file only once the chain tip has advanced that many blocks past the segment's last block. Until then the segment stays in the mutable pending area, so blocks still within reorg range can be overwritten. The default (256) is comfortably beyond mainnet's reorg depth; set it to 0 to seal as soon as a segment is complete.

--block-history N turns the archive into a rolling window: a background pruner keeps the most recent N blocks and deletes whole sealed segments below the floor, taking their hash-index entries, receipts markers and transaction counts with them. It is the master retention — --state-history and --receipts-history are capped to N, since history for a pruned block is useless — and it never runs ahead of the executor: with --execute on, a block is pruned only once it has been executed, so a node still catching up keeps everything until execution arrives. Nor ahead of the segment archive: with --backup, a sealed segment not backed up yet stays until it is. The floor is recorded in the MANIFEST before any file is removed, pruned blocks read as absent, backfill treats the floor as its lower bound, and debug_status reports it as blocks.prunedBelow. Pruning is one-way: a state rebuild from genesis is impossible afterwards, and turning execution on later than the floor leaves it unable to start. See Pruning.

See Block store for why, and Configuration for the YAML equivalents.

Execution & state

  • The execution subsystem is on by default: a worker trails the import workers, executing every stored block from genesis upward and maintaining the state store. Execution is verified against each header (receipts root, logs bloom, gas used) and halts loudly on divergence. Opt out with --execute=false when the node should only serve blocks/transactions.
  • With the default on, missing prerequisites (no block store, or a network without a built-in genesis or GenesisFile) log a warning and disable execution rather than failing startup; an explicit --execute turns those into hard errors.
  • The state can only be built once the block store is contiguous from genesis — run with --backfill reaching block 0. Before that, the worker idles and debug_status shows the execution section as not ready.
  • --execute-from / --execute-to are debug bounds on the executor, for re-running one block range under scrutiny; both default to off and change nothing then. --execute-to N stops execution after block N and leaves the state parked there. --execute-from N rebuilds from block N every time it is passed: an existing state is cut back to the post-state of N-1 (a deep unwind, through sealed changeset and receipts segments — the block store must still hold the blocks down to N-1) and refilled; a missing state is seeded with the genesis allocation as the post-state of N-1, so it is wrong by construction; such a state executes unverified — senders are patched so every transaction runs, header verification is off — until a rebuild from genesis replaces it. Drop both flags and the node carries on from whatever head the state has, as usual (an unverified state stays unverified). See Debugging a block range.
  • --state-history is the archive-vs-pruned trade-off: all (default) keeps every block traceable forever; a numeric window keeps the state DB small at the cost of historical reads below the floor. The flat latest state (and eth_call at the tip) is unaffected by pruning. With --block-history N the window is capped to N (see Pruning).
  • --receipts decides whether receipts are derived by execution into their own prunable stream (derive, the default), downloaded and stored with blocks (fetch), or not kept at all (none). Derive is the default whatever --execute says; fetching has to be asked for explicitly. With execution on, deriving means the fetcher skips receipts (about a quarter of the download, and eth/68 peers become fully useful), and receipts for a block become available once the executor has reached it. --receipts-history N then bounds the stream; a point lookup below the floor re-executes the block, a range query (eth_getLogs) does not — see Receipts and logs. Without execution, derive (still the default — the node warns at start) and none import blocks without receipts: the receipt and log methods answer not available and name the way out. Switching such a store to fetch backfills the missing receipts retroactively — a background worker fetches them from peers and attaches them in place, rewriting sealed segments — so nothing is lost by starting light (debug_receiptsStatus shows progress). A store built before the stream existed gains it on upgrade: the stream starts at the next segment boundary above the executed head and earlier blocks keep serving their inline receipts.

Segment archive

  • --backup turns on the backup of every sealed segment the node produces — block segments, and with --execute the state-history and receipts-stream segments — and of its state snapshots, to an S3 / R2 bucket or a local directory, uploaded as each segment seals; only data files go up, listed in the archive's manifest.json. Pruned segments stay archived, and pruning never drops a sealed segment the archive does not hold yet (see Pruning waits for the backup). One archive holds one chain and one layout per kind of segment: the uploader creates the manifest on a fresh archive and refuses an archive of another network or layout, so give each network its own root.
  • --archive-source is the archive the node downloads from — the public one by default, none for peers only. The backfill attaches whole sealed block segments from it instead of fetching their blocks from peers: each segment is verified (manifest hash, hash chain, header roots, linkage to the stored chain) before it is adopted, so it is as trustworthy as peer-fetched history, and an archive sealed with other store settings is re-packed into the local layout on the way in.
  • --syncmode is how an empty node gets its state: snap (the default) restores the archive's newest state snapshot and executes on from its head, downloading only the blocks the node keeps — --block-history bounds the backfill, --state-history / --receipts-history bound the history segments the restore fetches — and falls back to full (state built from genesis, every block needed) with a warning when the archive offers no snapshot. See Sync modes.
  • --s3-endpoint, --s3-region and --s3-path-style configure S3 access for both; credentials come from the AWS environment variables. See Segment archive.

JSON-RPC

  • --rpc-addr binds the JSON-RPC HTTP server. It is node-wide (like --discovery-addr) and so is flag-only — there is no per-network YAML equivalent. Set it to empty (--rpc-addr "") to disable RPC entirely.
  • --rpc-compression gzip-compresses responses, but only when the client sends Accept-Encoding: gzip and the payload is large enough to be worth it. See JSON-RPC for the method surface and request/response shapes.

Observability

  • Prometheus metrics are on by default at 0.0.0.0:18126. Turn them off with --metrics-enabled=false, or rebind with --metrics-addr. They expose the imported-head and ready-peer gauges and a per-method RPC latency histogram; block import and RPC calls are also timed in the logs. See Observability.
  • --log-p2p controls how chatty the p2p subsystem (devp2p server, discovery, and our peer connect/handshake/disconnect logs) is at debug. By default its debug lines are suppressed so --log-level debug shows the node's own work without peer/discovery churn; its info and above always show. Pass --log-p2p to add the p2p debug lines, or --log-level trace to show everything (the per-message wire firehose included). See Observability.