Skip to content

Performance

Optimizing blockchain node performance is crucial for maintaining network participation, reducing operational costs, and ensuring reliable RPC services. Blockchain nodes are resource-intensive applications that require careful tuning of memory, CPU, storage, and network parameters. This chapter provides comprehensive guidance on optimizing node performance across different hardware configurations and use cases.


Blockchain nodes have complex memory requirements:

┌─────────────────────────────────────────────────────────────┐
│ GETH MEMORY USAGE BREAKDOWN │
├─────────────────────────────────────────────────────────────┤
│ │
│ Total Memory: 16-64 GB │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Heap Memory │ │
│ │ ┌───────────┬───────────────┬───────────────┐ │ │
│ │ │ State │ Cache │ Trie Cache │ │ │
│ │ │ (EVM) │ (Blocks) │ (Merkle) │ │ │
│ │ │ ~4GB │ ~2GB │ ~2GB │ │ │
│ │ └───────────┴───────────────┴───────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Off-Heap Memory │ │
│ │ ┌───────────┬───────────────┬───────────────┐ │ │
│ │ │ LevelDB │ MDBX │ Snapshots │ │ │
│ │ │ Cache │ Cache │ (Read-Only) │ │ │
│ │ │ ~4GB │ ~4GB │ ~8GB │ │ │
│ │ └───────────┴───────────────┴───────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Terminal window
# Cache size in megabytes
# Default: 4096 MB (4 GB)
# For 16GB RAM system
geth --cache 4096
# For 32GB RAM system
geth --cache 8192
# For 64GB RAM system
geth --cache 16384
# For high-performance servers
geth --cache 32768
# View cache statistics
geth attach http://localhost:8545
> debug.getBlockChainInfo()
System RAMCacheGC SettingsNotes
8 GB2048DefaultMinimal configuration
16 GB4096DefaultStandard configuration
32 GB8192GOGC=200Good performance
64 GB16384GOGC=300High performance
128 GB32768GOGC=400Maximum performance
Terminal window
# High-memory configuration
export GOGC=300
export GOMEMLIMIT=32GiB
geth \
--cache 16384 \
--cache.database 8192 \
--cache.trie 4096 \
--cache.snapshot 25 # % of cache for snapshots (not MB; default 10%)
Terminal window
# Monitor memory usage
watch -n 5 'free -h'
# Detailed process memory
ps aux | grep geth
# Memory over time
pmap -x $(pgrep geth) | tail -1
# Check for memory leaks
journalctl -u geth | grep -i "memory"
journalctl -u geth | grep -i "gc"
# Using gotop (if installed)
gotop

┌─────────────────────────────────────────────────────────────┐
│ CPU OPTIMIZATION STRATEGY │
├─────────────────────────────────────────────────────────────┤
│ │
│ Blockchain Workload: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 40% - Cryptographic operations (hashing) │ │
│ │ 30% - Database I/O (LevelDB/RocksDB) │ │
│ │ 20% - Network processing │ │
│ │ 10% - State trie processing │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Recommendations: │
│ ✓ High single-thread performance is critical │
│ ✓ More cores help with parallel processing │
│ ✓ AVX2/AVX-512 instructions for crypto │
│ ✓ ECC RAM for stability │
│ │
└─────────────────────────────────────────────────────────────┘
Terminal window
# Limit CPU usage (useful for shared servers)
taskset -c 0-3 geth --syncmode snap
# View available cores
nproc
# Check CPU flags for crypto support
grep -o 'avx[0-9]*' /proc/cpuinfo | head -1
# Disable hyperthreading (sometimes helps)
# In BIOS, or via:
tuna --cores=0-7 geth
Terminal window
# GOMAXPROCS is a Go runtime setting, set via environment, not a geth CLI flag.
# The following is INVALID geth syntax — do not mix attach flags (--exec) with
# daemon flags (--gcmode):
# geth --gcmode=archive --exec "GOMAXPROCS=16" ❌
# Correct: set it in the environment / systemd unit
export GOMAXPROCS=16
# In systemd service:
Environment=GOMAXPROCS=16

Node TypeStorageGrowth/Year
Full Node~1.2 TB~100 GB
Archive Node~12 TB~500 GB
Pruned Node~400 GB~100 GB
Storage TypeRead IOPSWrite IOPSNotes
HDD100100Not recommended
SATA SSD50,00020,000Testnet only
NVMe SSD500,000+300,000+Required for production
NVMe (Gen4)700,000+500,000+Optimal
Terminal window
# Check current partition alignment
blockdev --report
# For NVMe, ensure 4K alignment
sudo fdisk -l /dev/nvme0n1
Terminal window
# Use ext4 or XFS (not BTRFS for database)
# Format with optimal settings
sudo mkfs.ext4 -E stride=128,stripe-width=128 /dev/nvme0n1
# Mount with noatime for better performance
# /etc/fstab:
# /dev/nvme0n1 /data ext4 defaults,noatime,nodiratime,discard 0 2
Terminal window
# Geth automatically tunes LevelDB
# But you can override:
# Cache for LevelDB (and trie/snapshot caches)
geth --cache 8192
# Number of open files (real flag: --fdlimit; --cache.fdlimit is not a geth flag)
geth --fdlimit 1024
Terminal window
# Erigon uses MDBX (memory-mapped B+ tree), NOT RocksDB — there is no
# --db.allow_unfastiate_geth flag and no "RocksDB tuning" for Erigon.
# Real tuning knobs:
erigon \
--db.size.limit=3TB \
--private.api.addr=localhost:9090 \
--prune htc
Terminal window
# Check disk I/O
iostat -x 5
# Check disk usage
df -h
# Check I/O wait (high = bottleneck)
vmstat 1
# Monitor disk queue
iostat -x 1 | grep -E "Device|vd[a-z]"
# Benchmark storage
fio --name=random_read --ioengine=libaio --iodepth=32 \
--bs=4k --size=1G --numjobs=4 --runtime=30 \
--readonly --filename=/tmp/fio_test

Node TypeMin BandwidthRecommended
Light Node10 Mbps100 Mbps
Full Node50 Mbps1 Gbps
Archive Node100 Mbps10 Gbps
Validator100 Mbps1 Gbps
Terminal window
# Increase file descriptors
echo "* soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "* hard nofile 65536" | sudo tee -a /etc/security/limits.conf
# For running user:
echo "$USER soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "$USER hard nofile 65536" | sudo tee -a /etc/security/limits.conf
# Increase network buffers
sudo sysctl -w net.core.rmem_max=16777216
sudo sysctl -w net.core.wmem_max=16777216
# Persist settings
echo "net.core.rmem_max=16777216" | sudo tee -a /etc/sysctl.conf
echo "net.core.wmem_max=16777216" | sudo tee -a /etc/sysctl.conf
Terminal window
# Increase max peers
geth --maxpeers 100
# Adjust dial metrics
geth --nat extip:YOUR_PUBLIC_IP
# Enable pprof (bound to localhost by default; --pprof.addr/--pprof.port configure it)
geth --pprof --pprof.addr localhost

[Unit]
Description=Ethereum Geth Node
After=network.target
[Service]
Type=simple
User=ethereum
WorkingDirectory=/home/ethereum
ExecStart=/usr/local/bin/geth \
--mainnet \
--syncmode snap \
--cache 16384 \
--cache.database 8192 \
--cache.trie 4096 \
--cache.snapshot 25 \
--maxpeers 100 \
--http \
--http.addr 0.0.0.0 \
--http.port 8545 \
--http.api eth,net,web3,debug,txpool \
--http.vhosts "*" \
--http.corsdomain "*" \
--ws \
--ws.addr 0.0.0.0 \
--ws.port 8546 \
--ws.api eth,net,web3 \
--ws.origins "*" \
--datadir /data/ethereum \
--keydir /data/ethereum/keystore \
--allow-insecure-unlock \
--rpc.gascap 50000000 \
--rpc.txfeecap 100 \
--ws.txfeecap 10 \
--pprof \
--pprof.addr localhost \
--pprof.port 6060
# Memory limits
MemoryMax=64G
MemoryHigh=48G
# CPU limits
CPUAffinity=0-15
# Restart settings
Restart=on-failure
RestartSec=5s
StandardOutput=append:/var/log/geth/output.log
StandardError=append:/var/log/geth/error.log
[Install]
WantedBy=multi-user.target
optimize_node.sh
#!/bin/bash
echo "=== Ethereum Node Performance Optimization ==="
# 1. Set kernel parameters
echo "Configuring kernel parameters..."
cat >> /etc/sysctl.conf << EOF
# Network optimizations
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
# File descriptor limits
fs.file-max=65536
# Blockchain specific
vm.max_map_count=1048576
EOF
sysctl -p
# 2. Set resource limits
echo "Configuring resource limits..."
cat >> /etc/security/limits.conf << EOF
* soft nofile 65536
* hard nofile 65536
* soft memlock unlimited
* hard memlock unlimited
EOF
# 3. CPU governor
echo "Setting CPU governor..."
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo performance > $cpu
done
# 4. Disable transparent huge pages
echo "Disabling transparent huge pages..."
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 5. Set IO scheduler (for SSDs)
echo "Setting IO scheduler..."
for disk in /sys/block/sd*/queue/scheduler; do
echo none > $disk
done
echo "=== Optimization Complete ==="
echo "Please restart your node for changes to take effect"

MetricGoodWarningCritical
CPU Usage< 60%60-80%> 80%
Memory Usage< 70%70-85%> 85%
Disk I/O Wait< 10%10-20%> 20%
Network InStableVariableDropping
Peer Count> 105-10< 5
# node_exporter config for system metrics
scrape_configs:
- job_name: 'node'
static_configs:
- targets: ['localhost:9100']
- job_name: 'geth'
static_configs:
- targets: ['localhost:6060']

Create a dashboard with these key queries:

# CPU Usage
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memory Usage
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100
# Disk I/O
rate(node_disk_read_bytes_total[5m])
rate(node_disk_written_bytes_total[5m])
# Network Traffic
rate(node_network_receive_bytes_total{device="eth0"}[5m])
rate(node_network_transmit_bytes_total{device="eth0"}[5m])
# Geth sync progress — there is no ethereum_block_height / ethereum_network_block
# pair. Use the node's own metrics: chain_head_block vs the highest header seen
# (a wide gap = node behind while the network advances):
chain_head_block
chain_head_header
# stalled-sync alert instead of a percentage:
increase(chain_head_block[10m]) == 0

Terminal window
# Check memory consumption
ps aux --sort=-%mem | head -10
# Check for memory leaks
journalctl -u geth | grep -i "out of memory"
# Reduce cache if needed
geth --cache 4096
Terminal window
# Find process using most CPU
top -o %CPU
# Check for runaway goroutines
curl http://localhost:6060/debug/pprof/goroutine?debug=1
# Reduce sync activity
geth --syncmode full
Terminal window
# Check I/O wait
iostat -x 1
# Find I/O heavy processes
iotop
# Consider upgrading to NVMe
lsblk -d -o NAME,TYPE,ROTA

System-level (kernel + limits) tuning complements client flags. These settings apply to any node daemon and generally use mainstream Linux sysctls.

Blockchain clients open many sockets and files. The default ulimit -n of 1024 is far too low:

/etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
root soft nofile 65536
root hard nofile 65536
Terminal window
# Apply to the current shell / service
ulimit -n 65536
# verify
ulimit -n
Terminal window
# /etc/sysctl.conf — socket buffers and backlog
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
net.core.netdev_max_backlog=50000
Terminal window
# Apply
sudo sysctl -p

Transparent Huge Pages (THP) can cause latency spikes for memory-heavy daemons (this is also the standard recommendation for PostgreSQL and Redis):

Terminal window
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

Some validators see more consistent performance with HT disabled, because concurrent threads on the same core compete for execution units, causing jitter in block-processing latency:

Terminal window
# Check current sibling layout
lscpu -e
# Symmetric multiprocessing off via kernel cmdline: add "nosmt" to GRUB_CMDLINE_LINUX
# /etc/default/grub → GRUB_CMDLINE_LINUX="nosmt" then update-grub
Terminal window
# Verify the governor
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# Pin to performance mode (cpupower from linux-tools)
sudo cpupower frequency-set -g performance

⚠️ Validators on consumer hardware should also disable C-states / use a stable clock if the firmware allows; clock jitter increases missed attesting slots in PoS networks.


  • Memory: Allocate appropriate cache based on available RAM (typically 50-75% of available memory)
  • CPU: Prioritize single-thread performance; more cores help with parallelism
  • Storage: NVMe SSDs are mandatory for production nodes
  • Network: Ensure adequate bandwidth and configure file descriptor limits
  • Monitoring: Track key metrics and set up alerts for degradation
  • Tuning: Use the comprehensive configuration examples provided for your hardware
  • Optimization: Run the optimization script for system-level improvements

In Chapter 48: Network Connectivity Issues, we’ll explore network troubleshooting.


Last Updated: 2026-10-07