Skip to main content
Version: v3.6

Database

Erigon stores all chain data under a single datadir. Understanding what lives where is useful when you size hardware, debug disk-usage issues, or split storage across fast and slow drives.

This page covers the what and where of Erigon's data. For the why — flat KV state, immutable snapshots — see Architecture. For operational tuning (symlinks, multi-disk layout), see Optimizing Storage.

The datadir at a glance​

datadir/
├── chaindata/ # Active state + recent blocks (MDBX). Small, hot, mutable.
├── snapshots/ # Historical data as immutable .seg files. Large, cold.
│ ├── domain/ # Latest value per domain (4 state domains + 2 receipt domains)
│ ├── history/ # Historical values per domain
│ ├── idx/ # Inverted indices — search/filter/intersect historical data
│ └── accessor/ # Random-access indices over history (point lookups only)
├── txpool/ # Pending transactions. Safe to delete; will repopulate from peers.
├── nodes/ # p2p peer cache. Safe to delete; will repopulate.
└── temp/ # External-sort buffers (~100 GB peak). Cleaned at startup.

The split between chaindata/ (mutable) and snapshots/ (immutable) is the central design decision. It is what makes Erigon's archive node 10× smaller than other clients while staying fast.

Storage engine: MDBX​

chaindata/ is a single MDBX key-value store. MDBX is a B+ tree engine derived from LMDB, optimised for read-heavy workloads and predictable memory use.

Properties that matter operationally:

  • No background compaction. Writes go directly into the B+ tree; there is no compaction thread that can spike CPU mid-RPC-call. This is why Erigon's RPC latency does not degrade under load the way LSM-based engines (LevelDB, RocksDB) can.
  • Memory-mapped reads. The OS page cache is the hot data cache. There is no separate per-process cache to tune. Multiple Erigon services on one machine share the same page cache automatically.
  • Single-writer. One process holds the write lock; readers are unlimited and lock-free. This is why splitting RPC Daemon out as a separate process for read scaling works cleanly — only one writer ever touches the file.

Snapshots: immutable history​

Older blocks and history are not stored in MDBX. They are written to .seg files in snapshots/ and never modified after creation.

Once a snapshot file is finalised it is the same bytes on every Erigon node in the world, identified by content hash. This unlocks two things:

  • BitTorrent distribution. New nodes fetch snapshots from peers in parallel rather than re-executing history from genesis. This is what OtterSync does.
  • Backup / disaster recovery costs ~10× less. Most of your datadir is content-addressed and can be re-downloaded from any peer if a disk fails. You only need to back up chaindata/.

Snapshots are organised into several subdirectories. The main ones are:

DirectoryWhat it holdsAccess pattern
snapshots/domain/Latest value per (domain, key). 6 domains: the 4 state domains (account, storage, code, commitment) plus 2 receipt domains (receipt, rcache)Sequential + point lookup
snapshots/history/Every historical value per (domain, key, txn)Point lookup keyed by transaction
snapshots/idx/Inverted indices over history — answers "which transactions touched key X?"Search / filter / set intersection
snapshots/accessor/Pre-built random-access indices over history filesRandom-touch point reads only

Per-transaction granularity. Erigon 3 indexes history at the transaction level, not the block level. This means:

  • You can replay a single historical transaction without re-executing its block.
  • If an account changes V1 → V2 → V1 within one block, debug_getModifiedAccountsByNumber correctly returns it.
  • Erigon stores compact per-transaction receipt metadata — cumulative gas used, blob gas used, log index — in a required receipt domain. Full receipts (with logs) live in a separate cache domain that is off by default in every prune mode (opt in with --prune.include-receipts). When a full receipt isn't cached, it is reconstructed on demand, re-deriving logs by re-execution.

What does it cost on disk?​

Real numbers from Erigon 3.6 archive nodes (--prune.mode=archive, default pruning options), July 2026:

eth-mainnetgnosissepoliahoodichiado
chaindata22.75 GB9.63 GB12.87 GB6.78 GB2.40 GB
snapshots (block segments)996.40 GB284.82 GB614.90 GB33.38 GB12.91 GB
snapshots/domain417.66 GB227.03 GB237.26 GB52.39 GB7.17 GB
snapshots/idx332.72 GB136.67 GB115.95 GB25.47 GB3.44 GB
snapshots/history255.60 GB39.28 GB68.93 GB8.00 GB2.33 GB
snapshots/accessor141.61 GB33.73 GB45.85 GB7.70 GB1.39 GB
total2.17 TB731.16 GB1.10 TB133.73 GB29.64 GB

Block segments — headers, bodies and transactions — sit directly in snapshots/ rather than in a sub-folder, and on mainnet they are the single largest item.

Data that is off by default is excluded above. On the same mainnet node it would add 441 GB for the receipts cache (--prune.include-receipts), 4.4 TB for commitment history (--prune.include-commitment-history), and 2.5 TB for the Caplin block, blob and state archive.

Each column is one node, so totals differ slightly from the headline figures on Hardware Requirements, which are measured on freshly synced nodes.

Why chaindata/ stays so small​

In Erigon 3, chaindata/ only holds:

  • The very latest state values (post-snapshot tip)
  • Recent blocks not yet folded into snapshots
  • Live txpool, peer state, sync stage progress

Most of the bulk that other clients keep in the active database — historical state, ancient blocks, receipt logs — is in immutable snapshot files instead. This is why chaindata/ rarely exceeds ~25 GB even on archive nodes.

Deleting chaindata/ is recoverable but not free: it discards the latest mutable state and any recent blocks not yet folded into snapshots. On restart Erigon re-derives state from the immutable snapshots (re-downloading them if needed) and resyncs the post-snapshot tip forward from the consensus layer over the Engine API — blocks are no longer pulled from peers over devp2p. Treat it as a resync of the chain tip, not a quick rebuild, and keep a backup if fast recovery matters.

Tuning knobs​

  • --batchSize — size of the Execution stage's in-memory buffer before it is flushed to MDBX. Default: 512M. Raising it (for example --batchSize 1G or higher) can speed up execution-heavy sync at the cost of more RAM. It is the Execution stage's commit threshold, so a larger value means larger single MDBX write transactions, which raise the file's high-water mark in bigger steps. Keeping it at or below 1G is a useful heuristic if you want MDBX to grow more gradually.
  • --db.size.limit — caps the MDBX file size. Useful when running multiple Erigon instances on one disk to prevent one from starving the others.
  • --db.read.concurrency — maximum number of concurrent open MDBX read transactions (the read-tx semaphore). Raise it for nodes serving heavy parallel RPC (for example a high-throughput RPC daemon against the same datadir); low values are fine for low-read-concurrency nodes such as validators.
  • Symlinks for tiered storage. Place chaindata/ and snapshots/domain/ on fast NVMe, leave snapshots/idx/ and snapshots/history/ on cheaper SATA. See Optimizing Storage for the recipe.

Safe-to-delete subdirectories​

If you need to reclaim space without resyncing from scratch:

DirectoryEffect of deletion
txpool/Pending transactions lost; pool refills from peers within minutes
nodes/Peer cache lost; reconnects on restart
temp/Cleaned automatically at startup anyway
chaindata/Recoverable, but triggers a resync of the post-snapshot tip from the consensus layer — not instant; keep a backup for fast recovery
snapshots/Do not delete — would force a full resync

Where to go next​