Skip to main content

Espresso Mainnet 1 Node Setup Guide

This guide covers deploying an Espresso Mainnet 1 Regular (Non-DA) Node using Docker Compose. For validator registration and key generation, see the Validator Registration guide first.

Prerequisites

System Requirements

ComponentRegular NodeDA Node
CPU1+ Core4+ Core (node) + 2 Core (DB)
RAM8 GB8 GB (node) + 4 GB (DB)
StorageNegligible (kilobytes)1.2 TB SSD (archive) / 100 GB (pruning)
OSUbuntu 20.04/22.04 LTSUbuntu 20.04/22.04 LTS
NetworkStable internet, public IPStable internet, public IP

Software Requirements

  • Docker Engine 24+ and Docker Compose V2
  • Ethereum L1 RPC endpoints (HTTP and WebSocket)

Before You Begin

Ensure you have completed:

  1. Generated consensus keys (BLS + Schnorr)
  2. Registered your validator in the Stake Table
  3. Delegated stake to your node

Network Ports

PortProtocolPurposeRequired
30000UDP + TCPLibp2p P2P✅ Must be open and reachable
26000TCPAPI / Metrics / HealthcheckRecommended for monitoring
warning

The Libp2p port (default 30000) must be publicly reachable on both UDP and TCP. Without this, your node cannot communicate with other validators.

Step 1: Prepare Project Directory

sudo -i

mkdir -p /data/espresso/{data,keys}
cd /data/espresso

Step 2: Create Environment File

.env
###############################################################################
# Espresso Mainnet 1 Node - Environment Configuration
###############################################################################

#--- Ethereum L1 RPC -----------------------------------------------------------
# HTTP JSON-RPC endpoint (required)
ESPRESSO_SEQUENCER_L1_PROVIDER=https://mainnet.infura.io/v3/<YOUR-API-KEY>

# WebSocket endpoint (required for Mainnet 1 operations)
ESPRESSO_SEQUENCER_L1_WS_PROVIDER=wss://mainnet.infura.io/v3/<YOUR-API-KEY>

#--- Networking ----------------------------------------------------------------
# Your server's public IP address (required, other nodes need to reach you)
ESPRESSO_PUBLIC_IP=YOUR_SERVER_PUBLIC_IP

# Libp2p P2P port (UDP + TCP, must be open in firewall)
ESPRESSO_LIBP2P_PORT=30000

# Host port for the API (metrics, healthcheck)
ESPRESSO_API_PORT=26000
tip

Replace <YOUR-API-KEY> with your actual Infura/Alchemy API key and YOUR_SERVER_PUBLIC_IP with your server's public IP address.

Step 3: Place Your Key File

Copy your generated key file into the keys/ directory:

# If you generated keys with the keygen command:
cp /path/to/your/generated/keys/0.env /data/espresso/keys/0.env
chmod -R 0700 /data/espresso/keys

The key file should contain:

keys/0.env
ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=BLS_SIGNING_KEY~<your-bls-private-key>
ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=SCHNORR_SIGNING_KEY~<your-schnorr-private-key>
danger

Protect this file — it contains your consensus private keys. Set strict permissions (chmod 600) and never commit it to version control.

Step 4: Create Docker Compose File

docker-compose.yml
###############################################################################
# Espresso Mainnet 1 - Regular (Non-DA) Node
# Docs: https://docs.espressosys.com/network/guides/node-operators/running-a-mainnet-1-node
###############################################################################

services:
espresso-node:
image: ghcr.io/espressosystems/espresso-sequencer/sequencer:20260407
container_name: espresso-sequencer
restart: unless-stopped
command: sequencer -- http -- catchup -- status

environment:
#---------------------------------------------------------------------------
# Fixed / Network-wide (DO NOT CHANGE)
#---------------------------------------------------------------------------
ESPRESSO_SEQUENCER_CDN_ENDPOINT: cdn.main.net.espresso.network:1737
ESPRESSO_STATE_RELAY_SERVER_URL: https://state-relay.main.net.espresso.network
ESPRESSO_SEQUENCER_GENESIS_FILE: /genesis/mainnet.toml
ESPRESSO_SEQUENCER_EMBEDDED_DB: "true"
RUST_LOG: "warn,libp2p=off"
RUST_LOG_FORMAT: json

# State peers (at least one required)
ESPRESSO_SEQUENCER_STATE_PEERS: https://query.main.net.espresso.network

# Config peers (required for first-time startup)
ESPRESSO_SEQUENCER_CONFIG_PEERS: https://cache.main.net.espresso.network

#---------------------------------------------------------------------------
# Operator-specific (FILL THESE IN via .env file)
#---------------------------------------------------------------------------
ESPRESSO_SEQUENCER_L1_PROVIDER: ${ESPRESSO_SEQUENCER_L1_PROVIDER:?set ESPRESSO_SEQUENCER_L1_PROVIDER in .env}
ESPRESSO_SEQUENCER_L1_WS_PROVIDER: ${ESPRESSO_SEQUENCER_L1_WS_PROVIDER:?set ESPRESSO_SEQUENCER_L1_WS_PROVIDER in .env}
ESPRESSO_SEQUENCER_API_PORT: 80
ESPRESSO_SEQUENCER_STORAGE_PATH: /data/store/
ESPRESSO_SEQUENCER_KEY_FILE: /keys/0.env
ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS: 0.0.0.0:${ESPRESSO_LIBP2P_PORT:-30000}
ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS: ${ESPRESSO_PUBLIC_IP}:${ESPRESSO_LIBP2P_PORT:-30000}

ports:
# API port (metrics, healthchecks)
- "${ESPRESSO_API_PORT:-26000}:80"
# Libp2p P2P port (UDP)
- "${ESPRESSO_LIBP2P_PORT:-30000}:${ESPRESSO_LIBP2P_PORT:-30000}/udp"
# Libp2p P2P port (TCP)
- "${ESPRESSO_LIBP2P_PORT:-30000}:${ESPRESSO_LIBP2P_PORT:-30000}/tcp"

volumes:
# Persistent storage for embedded SQLite DB
- ./data:/data/store
# Key file (mounted read-only)
- ./keys:/keys:ro

logging:
driver: json-file
options:
max-size: "100m"
max-file: "5"

healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/healthcheck"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s

Environment Variables Reference

Fixed (Network-wide)

VariableValueDescription
ESPRESSO_SEQUENCER_CDN_ENDPOINTcdn.main.net.espresso.network:1737CDN for block delivery
ESPRESSO_STATE_RELAY_SERVER_URLhttps://state-relay.main.net.espresso.networkState relay server
ESPRESSO_SEQUENCER_GENESIS_FILE/genesis/mainnet.tomlGenesis config (built into image)
ESPRESSO_SEQUENCER_EMBEDDED_DBtrueUse embedded SQLite for Regular Node
ESPRESSO_SEQUENCER_STATE_PEERShttps://query.main.net.espresso.networkState sync peers
ESPRESSO_SEQUENCER_CONFIG_PEERShttps://cache.main.net.espresso.networkConfig bootstrap peers (first startup only)

Operator-specific (.env file)

VariableExampleDescription
ESPRESSO_SEQUENCER_L1_PROVIDERhttps://mainnet.infura.io/v3/<KEY>HTTP JSON-RPC for Ethereum mainnet (required)
ESPRESSO_SEQUENCER_L1_WS_PROVIDERwss://mainnet.infura.io/v3/<KEY>WebSocket for Ethereum Mainnet
ESPRESSO_PUBLIC_IP203.0.113.1Server public IP address
ESPRESSO_LIBP2P_PORT30000Libp2p P2P port (UDP + TCP)
ESPRESSO_API_PORT26000Host port for API / metrics / healthcheck
info

Alternative to key file: Instead of ESPRESSO_SEQUENCER_KEY_FILE, you can set the two key environment variables directly:

  • ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=BLS_SIGNING_KEY~...
  • ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=SCHNORR_SIGNING_KEY~...

Step 5: Open Firewall Ports

# UFW (Ubuntu)
ufw allow 30000/udp
ufw allow 30000/tcp
ufw allow 26000/tcp # API/metrics/healthcheck for monitoring

# Or iptables
iptables -A INPUT -p udp --dport 30000 -j ACCEPT
iptables -A INPUT -p tcp --dport 30000 -j ACCEPT

Step 6: Launch the Node

warning

Ensure the data and keys directories exist in your current directory (/data/espresso) before starting the node. If Docker creates them automatically, it may assign incorrect permissions. (You should have created these in Step 1).

cd /data/espresso

# Start the node in detached mode
docker compose up -d

# Verify the container is running
docker compose ps

Expected output:

NAME                IMAGE                                                            STATUS          PORTS
espresso-sequencer ghcr.io/espressosystems/espresso-sequencer/sequencer:20260407 Up 10 seconds 0.0.0.0:26000->80/tcp, 0.0.0.0:30000->30000/udp+tcp

Step 7: Monitor the Node

Check Logs

# Follow real-time logs
docker compose logs -f

# Check last 100 lines
docker compose logs --tail 100

Healthcheck

curl -f http://localhost:26000/healthcheck

curl -s http://localhost:26000/status | jq .

Key Log Messages

On first startup, look for these indicators:

Log MessageMeaning
Fetching config from peersNode is bootstrapping network config
Connected to CDNSuccessfully joined the CDN network
Joined consensusNode is participating in consensus
Received proposalNode is receiving and processing blocks
tip

First startup may take a few minutes as the node fetches network configuration from ESPRESSO_SEQUENCER_CONFIG_PEERS. After the first successful join, this config is cached locally and the config peers parameter is no longer needed on restarts.

Directory Structure

/data/espresso/
├── .env # Environment variables
├── docker-compose.yml # Service definition
├── data/ # Persistent SQLite DB storage
└── keys/
└── 0.env # Consensus private keys (BLS + Schnorr)

Keep /data/espresso/data persistent across restarts. Keep /data/espresso/keys/0.env backed up offline and excluded from Git; it contains the BLS and Schnorr private keys used by the node.

Operations

Restart the Node

docker compose restart

Stop the Node

docker compose down

Upgrade the Node

When a new release is published:

  1. Update the image tag in docker-compose.yml
  2. Pull and restart:
docker compose pull
docker compose up -d

View Resource Usage

docker stats espresso-sequencer --no-stream

Backup

The key file is the most critical piece to back up:

# Back up keys (store securely, encrypted)
cp /data/espresso/keys/0.env /secure-backup/espresso-keys-backup.env

Troubleshooting

Common Issues

IssueCauseSolution
Connection refused on P2PFirewall blocking UDP/TCP 30000Open port 30000 on both protocols
Failed to fetch configFirst-time config peers unreachableVerify internet connectivity and DNS resolution
Node not in active setInsufficient delegated stakeNeed to be in top 100 by delegated ESP tokens
L1 provider errorInvalid or rate-limited RPCCheck your Infura/Alchemy API key and plan limits
Healthcheck failingNode still syncingWait for initial sync to complete (~2-5 min)

Debug Logging

To enable verbose logging temporarily:

docker compose down
# Edit docker-compose.yml: change RUST_LOG to "info,libp2p=off"
docker compose up -d
docker compose logs -f

Mainnet 1 Key Changes from Mainnet 0

If migrating from Mainnet 0:

ChangeDetails
Container imageUpdated to 20260407
Peer URLsUse v1 APIs for all peer URLs
stateAPI moduleNow auto-enabled — do NOT specify state on command line
L1 WS ProviderESPRESSO_SEQUENCER_L1_WS_PROVIDER should be configured alongside ESPRESSO_SEQUENCER_L1_PROVIDER