Skip to content

Configuring Reth

Reth places a configuration file named reth.toml in the data directory specified when starting the node. It is written in the TOML format.

The default data directory is platform dependent:

  • Linux: $XDG_DATA_HOME/reth/ or $HOME/.local/share/reth/
  • Windows: {FOLDERID_RoamingAppData}/reth/
  • macOS: $HOME/Library/Application Support/reth/

The configuration file contains the following sections:

The [stages] section

The stages section is used to configure how individual stages in reth behave, which has a direct impact on resource utilization and sync speed.

The defaults shipped with Reth try to be relatively reasonable, but may not be optimal for your specific set of hardware.

era

The ERA stage configures pre-synced ERA1 data ingestion, either from a local directory or a remote host.

[stages.era]
# Use a local directory containing ERA1 files (conflicts with `url`)
path = "/path/to/era1"
# Or download ERA1 files from a host (conflicts with `path`)
# url = "https://example.com/era1/"
# When using `url`, specify a temporary download folder
# folder = "/path/to/reth/era"

headers

The headers section controls both the behavior of the header stage, which downloads historical headers, as well as the primary downloader that fetches headers over P2P.

[stages.headers]
# The minimum and maximum number of concurrent requests to have in flight at a time.
#
# The downloader uses these as best effort targets, which means that the number
# of requests may be outside of these thresholds within a reasonable degree.
#
# Increase these for faster sync speeds at the cost of additional bandwidth and memory
downloader_max_concurrent_requests = 100
downloader_min_concurrent_requests = 5
# The maximum number of responses to buffer in the downloader at any one time.
#
# If the buffer is full, no more requests will be sent until room opens up.
#
# Increase the value for a larger buffer at the cost of additional memory consumption
downloader_max_buffered_responses = 100
# The maximum number of headers to request from a peer at a time.
downloader_request_limit = 1000
# The amount of headers to persist to disk at a time.
#
# Lower thresholds correspond to more frequent disk I/O (writes),
# but lowers memory usage
commit_threshold = 10000

bodies

The bodies section controls both the behavior of the bodies stage, which downloads historical block bodies, as well as the primary downloader that fetches block bodies over P2P.

[stages.bodies]
# The maximum number of bodies to request from a peer at a time.
downloader_request_limit = 200
# The maximum amount of bodies to download before writing them to disk.
#
# A lower value means more frequent disk I/O (writes), but also
# lowers memory usage.
downloader_stream_batch_size = 1000
# The size of the internal block buffer in bytes.
#
# A bigger buffer means that bandwidth can be saturated for longer periods,
# but also increases memory consumption.
#
# If the buffer is full, no more requests will be made to peers until
# space is made for new blocks in the buffer.
#
# Defaults to around 2GB.
downloader_max_buffered_blocks_size_bytes = 2147483648
# The minimum and maximum number of concurrent requests to have in flight at a time.
#
# The downloader uses these as best effort targets, which means that the number
# of requests may be outside of these thresholds within a reasonable degree.
#
# Increase these for faster sync speeds at the cost of additional bandwidth and memory
downloader_min_concurrent_requests = 5
downloader_max_concurrent_requests = 100

sender_recovery

The sender recovery stage recovers the address of transaction senders using transaction signatures.

[stages.sender_recovery]
# The number of transactions to recover senders for before
# writing the results to disk.
#
# Lower thresholds correspond to more frequent disk I/O (writes),
# but lowers memory usage
commit_threshold = 5000000

execution

The execution stage executes historical transactions. This stage is generally very I/O and memory intensive, since executing transactions involves reading block headers, transactions, accounts and account storage.

Each executed transaction also generates a number of changesets, and mutates the current state of accounts and storage.

For this reason, there are several ways to control how much work to perform before the results are written to disk.

[stages.execution]
# The maximum number of blocks to process before the execution stage commits.
max_blocks = 500000
# The maximum number of state changes to keep in memory before the execution stage commits.
max_changes = 5000000
# The maximum cumulative amount of gas to process before the execution stage commits.
max_cumulative_gas = 1500000000 # 30_000_000 * 50_000
# The maximum time spent on blocks processing before the execution stage commits.
max_duration = '10m'

For all thresholds specified, the first to be hit will determine when the results are written to disk.

Lower values correspond to more frequent disk writes, but also lower memory consumption. A lower value also negatively impacts sync speed, since reth keeps a cache around for the entire duration of blocks executed in the same range.

prune

Controls how frequently the prune stage commits its progress.

[stages.prune]
# The maximum number of entries to prune before committing progress to the database.
commit_threshold = 1_000_000

account_hashing

The account hashing stage builds a secondary table of accounts, where the key is the hash of the address instead of the raw address.

This is used to later compute the state root.

[stages.account_hashing]
# The threshold in number of blocks before the stage starts from scratch
# and re-hashes all accounts as opposed to just the accounts that changed.
clean_threshold = 500000
# The amount of accounts to process before writing the results to disk.
#
# Lower thresholds correspond to more frequent disk I/O (writes),
# but lowers memory usage
commit_threshold = 100000

storage_hashing

The storage hashing stage builds a secondary table of account storages, where the key is the hash of the address and the slot, instead of the raw address and slot.

This is used to later compute the state root.

[stages.storage_hashing]
# The threshold in number of blocks before the stage starts from scratch
# and re-hashes all storages as opposed to just the storages that changed.
clean_threshold = 500000
# The amount of storage slots to process before writing the results to disk.
#
# Lower thresholds correspond to more frequent disk I/O (writes),
# but lowers memory usage
commit_threshold = 100000

merkle

The merkle stage uses the indexes built in the hashing stages (storage and account hashing) to compute the state root of the latest block.

[stages.merkle]
# The number of blocks to run the incremental root method for when catching up.
# When syncing a large number of blocks, incremental root building is limited
# to prevent memory issues.
incremental_threshold = 7000
# The threshold in number of blocks before the stage starts from scratch
# and rebuilds the entire trie, discarding the existing trie.
rebuild_threshold = 100000

transaction_lookup

The transaction lookup stage builds an index of transaction hashes to their sequential transaction ID.

[stages.transaction_lookup]
# The maximum number of transactions to process before writing the results to disk.
#
# Lower thresholds correspond to more frequent disk I/O (writes),
# but lowers memory usage
chunk_size = 5000000

index_account_history

The account history indexing stage builds an index of what blocks a particular account changed.

[stages.index_account_history]
# The maximum amount of blocks to process before writing the results to disk.
#
# Lower thresholds correspond to more frequent disk I/O (writes),
# but lowers memory usage
commit_threshold = 100000

index_storage_history

The storage history indexing stage builds an index of what blocks a particular storage slot changed.

[stages.index_storage_history]
# The maximum amount of blocks to process before writing the results to disk.
#
# Lower thresholds correspond to more frequent disk I/O (writes),
# but lowers memory usage
commit_threshold = 100000

etl

An ETL (extract, transform, load) data collector. Used mainly to insert data into MDBX in a sorted manner.

[stages.etl]
# Optional directory for temporary files used by ETL. Defaults to `datadir/etl-tmp` when unset.
# dir = "/path/to/reth/etl-tmp"
# The maximum size in bytes of data held in memory before being flushed to disk as a file.
#
# Lower threshold corresponds to more frequent flushes,
# but lowers temporary storage usage
file_size = 524_288_000 # 500 * 1024 * 1024

The [peers] section

The peers section is used to configure how the networking component of reth establishes and maintains connections to peers.

In the top level of the section you can configure trusted nodes, and how often reth will try to connect to new peers.

[peers]
# How often reth will attempt to make outgoing connections,
# if there is room for more peers
refill_slots_interval = '5s'
# A list of ENRs for trusted peers, which are peers reth will always try to connect to.
trusted_nodes = []
# Whether reth will only attempt to connect to the peers specified above,
# or if it will connect to other peers in the network
connect_trusted_nodes_only = false
# Maximum number of backoff attempts before we drop a non-trusted peer
max_backoff_count = 5
# DNS resolution refresh interval for trusted nodes
trusted_nodes_resolution_interval = '1h'
# The duration for which a badly behaving peer is banned
ban_duration = '12h'
# Temporary per-IP throttle for inbound connection attempts
incoming_ip_throttle_duration = '30s'

connection_info

This section configures how many peers reth will connect to.

[peers.connection_info]
# The maximum number of outbound peers (peers we connect to)
max_outbound = 100
# The maximum number of inbound peers (peers that connect to us)
max_inbound = 30
# The maximum number of concurrent outbound dials performed at once
max_concurrent_outbound_dials = 15

reputation_weights

This section configures the penalty for various offences peers can commit.

All peers start out with a reputation of 0, which increases over time as the peer stays connected to us.

If the peer misbehaves, various penalties are exacted to their reputation, and if it falls below a certain threshold (currently 50 * -1024), reth will disconnect and ban the peer temporarily (except for protocol violations which constitute a permanent ban).

[peers.reputation_weights]
bad_message = -16384
bad_block = -16384
bad_transactions = -16384
already_seen_transactions = 0
timeout = -4096
bad_protocol = -2147483648
failed_to_connect = -25600
dropped = -4096
bad_announcement = -1024

backoff_durations

If reth fails to establish a connection to a peer, it will not re-attempt for some amount of time, depending on the reason the connection failed.

[peers.backoff_durations]
low = '30s'
medium = '3m'
high = '15m'
max = '1h'

The [sessions] section

The sessions section configures the internal behavior of a single peer-to-peer connection.

You can configure the session buffer sizes, which limits the amount of pending events (incoming messages) and commands (outgoing messages) each session can hold before it will start to ignore messages.

Note

These buffers are allocated per peer, which means that increasing the buffer sizes can have large impact on memory consumption.

[sessions]
session_command_buffer = 32
session_event_buffer = 260

You can also configure request timeouts:

[sessions.initial_internal_request_timeout]
secs = 20
nanos = 0
 
# The amount of time before the peer will be penalized for
# being in violation of the protocol. This exacts a permaban on the peer.
[sessions.protocol_breach_request_timeout]
secs = 120
nanos = 0

Additionally, you can configure when pending sessions time out, and enforce optional per-state limits.

# Timeout after which a pending session attempt is considered failed
[sessions.pending_session_timeout]
secs = 20
nanos = 0
 
# Optional limits (no limits are enforced by default when unset)
[sessions.limits]
max_pending_inbound = 100
max_pending_outbound = 50
max_established_inbound = 100
max_established_outbound = 50

The [prune] section

The prune section configures the pruning configuration.

You can configure the pruning of different segments of the data independently of others. For any unspecified segments, the default setting is no pruning.

Default config

No pruning, run as archive node.

Example of the custom pruning configuration

This configuration will:

  • Run pruning every 5 blocks
  • Continuously prune all transaction senders, account history, storage history and bodies history before the block head-100_000, i.e. keep the data for the last 100_000 blocks
  • Prune all receipts before the block 1920000, i.e. keep receipts from the block 1920000
  • Keep the last 128 blocks of merkle changesets (default behavior)
[prune]
# Minimum pruning interval measured in blocks
block_interval = 5
 
[prune.segments]
# Sender Recovery pruning configuration
sender_recovery = { distance = 100_000 } # Prune all transaction senders before the block `head-100000`, i.e. keep transaction senders for the last 100001 blocks
 
# Transaction Lookup pruning configuration
transaction_lookup = "full" # Prune all TxNumber => TxHash mappings
 
# Receipts pruning configuration. This setting overrides `receipts_log_filter`.
receipts = { before = 1920000 } # Prune all receipts from transactions before the block 1920000, i.e. keep receipts from the block 1920000
 
# Account History pruning configuration
account_history = { distance = 100_000 } # Prune all historical account states before the block `head-100000`
 
# Storage History pruning configuration
storage_history = { distance = 100_000 } # Prune all historical storage states before the block `head-100000`
 
# Bodies History pruning configuration
bodies_history = { distance = 100_000 } # Prune all historical block bodies before the block `head-100000`
 
# Merkle Changesets pruning configuration
# Controls pruning of AccountsTrieChangeSets and StoragesTrieChangeSets.
# Default: { distance = 128 } - keeps the last 128 blocks of merkle changesets
merkle_changesets = { distance = 128 }

We can also prune receipts more granular, using the logs filtering:

# Receipts pruning configuration by retaining only those receipts that contain logs emitted
# by the specified addresses, discarding all others. This setting is overridden by `receipts`.
[prune.segments.receipts_log_filter]
# Prune all receipts, leaving only those which:
# - Contain logs from address `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`, starting from the block 17000000
# - Contain logs from address `0xdac17f958d2ee523a2206206994597c13d831ec7` in the last 1001 blocks
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" = { before = 17000000 }
"0xdac17f958d2ee523a2206206994597c13d831ec7" = { distance = 1000 }

The [static_files] section

Configure static file segmentation.

[static_files.blocks_per_file]
# Number of blocks per file for each segment (optional)
# Values must be greater than 0 if set
headers = 8192
transactions = 8192
receipts = 8192
transaction_senders = 8192