Beta v4 migration guide
Linea Beta v4 is a mandatory hard fork introducing Maru, the new consensus layer (CL).
Maru replaces Clique and aligns Linea with Ethereum's dual-layer design (execution and consensus).
After the fork:
- You must run both an execution layer (EL) client and Maru, a CL client
- Sequencer signatures move from EL
extraData
to the Maru attestations API. - EL clients must be upgraded to Beta v4 compatible versions (any EL client that supports Prague should be compatible).
See the Beta v4 release notes for the scheduled hard fork dates.
Breaking change: sequencer signatures
Sequencer signatures will no longer be in EL block extraData
after the
upgrade.
Before (pre-fork):
// on EL client
let signatures = block.extra_data;
After (post-fork):
// Must switch to Maru API
let signatures = maru_api.get("/eth/v2/beacon/blocks/{block_id}").data.message.body.attestations;
attestations
returns a list containing the current block signature/attestation
and the previous block signature/attestation.
Key architecture changes
Current
- Single layer: EL client only (Besu with Clique)
- Sequencer signatures: Stored in EL block
extraData
- Block production: Clique handles execution and consensus
After Beta v4
- Dual-layer architecture: Execution Layer (EL) client + Consensus Layer (CL) client (The CL is currently powered by Maru, a consensus client implementing a customized variation of the QBFT algorithm. While Maru is the only supported client today, the architecture is designed to support future client diversity and algorithm evolution).
- Sequencer signatures: Moved to Maru's
SealedBeaconBlock.commitSeals
- Block production: Maru (QBFT) coordinates consensus, EL executes transactions
- API: Signatures and consensus data available via Maru APIs
EL client compatibility
By design, Linea with Maru will support any client compatible with L1. So any vanilla client compatible with Prague will work.
Supported EL clients (from e2e tests):
- Besu: 25.8.0 or higher
- Geth: Use the latest release (downtime required)
- Erigon: Use the latest release
- Nethermind: Use the latest release
- Linea-specific: A new
linea-besu-package
release is available via Docker:consensys/linea-besu-package:beta-v4.0-rc11-20251004063519-a5c4c31
⚠️ Geth-specific issue:
- Geth v1.15+ → incompatible with Clique (pre-fork)
- Geth v1.13 → incompatible with Prague (post-fork)
- Result: No zero-downtime upgrade path; must plan maintenance window.
Upgrade strategy
Depending on your EL client, follow the relevant upgrade path:
Besu, Erigon, Nethermind (zero-downtime)
Pre-upgrade
- Prepare Docker setup (EL + Maru)
- Upgrade EL client to Prague-compatible version
During upgrade
- Start Maru before the fork timestamp
- Keep EL client running
- Monitor logs for sync and consensus:
docker logs linea-maru | grep "imported block"
curl -s -X POST http://localhost:8545 -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
Post-upgrade
- Verify block import in Maru logs
- Confirm EL block height with
eth_blockNumber
⚠️ Geth (downtime required):
Pre-upgrade
- Prepare Docker setup (EL + Maru)
- Plan a short maintenance window after the fork
- Note: the genesis file provided for Besu should be used for Geth as well.
During upgrade
- Stop Geth after fork timestamp
- Upgrade to Geth v1.15+ (Prague-compatible)
- Start both Maru and Geth together
Post-upgrade
- Same verification steps as above
Installation and setup guide
Prerequisites
- Docker and Docker Compose (https://hub.docker.com/r/consensys/maru/tags or https://github.com/Consensys/maru)
- 8GB+ RAM (16GB recommended)
- Open ports: 8545, 8550, 8080, 9090, 9000
- Synced EL client (for example Besu Sepolia)
- L1 (Sepolia) RPC URL — required for Maru to track zk-proof verification and determine which L2 blocks are finalized
Step 1: Directory structure
mkdir -p ~/linea-node/{maru/config,besu/config}
cd ~/linea-node
Step 2: Besu configuration (Sepolia)
Create besu/config/config.toml
:
# Sepolia configuration
data-path="/opt/besu/data"
genesis-file="/opt/besu/genesis.json"
# Sepolia bootnodes
bootnodes=[
"enode://6f20afbe4397e51b717a7c1ad3095e79aee48c835eebd9237a3e8a16951ade1fe0e66e981e30ea269849fcb6ba03d838da37f524fabd2a557474194a2e2604fa@18.221.100.27:31002",
"enode://ce1e0d8e0500cb5c0ac56bdcdafb2d6320c3a2c5125b5ccf12f5dfc9b47ee74acbcafc32559017613136c9c36a0ce74ba4f83b7fb8244f099f3b15708d9d3129@3.23.75.47:31000",
"enode://1b026a5eb0ae74300f58987d235ef0e3a550df963345cb3574be3b0b54378bd11f14dfd515a8976f2c2d2826090e9507b8ccc24f896a9ffffffcabcfd996a733@3.129.120.128:31001"
]
# Sync configuration
sync-mode="SNAP"
data-storage-format="BONSAI"
# P2P
p2p-port=30303
p2p-host="YOUR_PUBLIC_IP" # Replace with your machine public IP
host-allowlist=["*"]
discovery-enabled=true
fast-sync-min-peers=1
# Engine API (CRITICAL for Maru connection)
engine-host-allowlist=["*"]
engine-rpc-port=8550
engine-jwt-disabled=true
engine-rpc-enabled=true # No JWT required for development/testing
# JSON-RPC
rpc-http-enabled=true
rpc-http-host="0.0.0.0"
rpc-http-port=8545
rpc-http-cors-origins=["*"]
rpc-http-api=["ENGINE","DEBUG","NET","ETH","WEB3"]
Update Besu genesis.json
to the following:
{
"config": {
"chainId": 59141,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"terminalTotalDifficulty": 37331807,
"shanghaiTime": 1759147200,
"cancunTime": 1759233600,
"pragueTime": 1760004000,
"depositContractAddress": "0x0bdae9550159eb73559e74d27fbc989641b24c8e",
"withdrawalRequestContractAddress": "0x0e97F666F1d99A60590F7Ce7fA5A5a7C754Ff714",
"consolidationRequestContractAddress": "0x94d94fac95855ed81294ed76f6b3da899e7063a8",
"clique": {
"createemptyblocks": true,
"blockperiodseconds": 1,
"epochlength": 30000
},
"blobSchedule": {
"cancun": {
"target": 0,
"max": 0,
"baseFeeUpdateFraction": 3338477
},
"prague": {
"target": 0,
"max": 0,
"baseFeeUpdateFraction": 3338477
}
}
},
"nonce": "0x0",
"timestamp": "0x6391BFF3",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000a27342f1b74c0cfb2cda74bac1628d0c1a9752f20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x3A2C940",
"baseFeePerGas": "0x8",
"difficulty": "0x1",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"Ed8587afbDBb2504EC31583B3377447d6fA322B2": {
"balance": "0x8AC7230489E80000"
},
"971e727e956690b9957be6d51Ec16E73AcAC83A7": {
"balance": "0x33B2E3C9FD0803CE8000000"
},
"47C63d1E391FcB3dCdC40C4d7fA58ADb172f8c38": {
"balance": "0x8AC7230489E80000"
},
"5948ccC8A59B464c6DcC87db4004Cf0a8d600C93": {
"balance": "0x8AC7230489E80000"
},
"73259D1d7534D1b68542e3240Cf76A03a5C2530F": {
"balance": "0x8AC7230489E80000"
},
"857dBFEbD7D29a75FF06d1423c418Da3b6E07292": {
"balance": "0x8AC7230489E80000"
},
"C702eAfe94b232ed50A7d4aC204306b97cdDAB0B": {
"balance": "0x8AC7230489E80000"
},
"Cc791070a5aA20f7C77CFd02E1713AfA30A23021": {
"balance": "0x8AC7230489E80000"
},
"Cd6366E24283D5D2fA10350Ac75d8a0B798E1FAe": {
"balance": "0x8AC7230489E80000"
},
"d83C3dC7257Eb2E304CBEB50b0B98bc2dd2cdCd9": {
"balance": "0x8AC7230489E80000"
},
"df5443dBe811fF9a121466dDa3C96292a3F6f43D": {
"balance": "0x8AC7230489E80000"
},
"0000000000000000000000000000000000000000": {
"balance": "0x1"
},
"0000000000000000000000000000000000000001": {
"balance": "0x1"
},
"0000000000000000000000000000000000000002": {
"balance": "0x1"
},
"0000000000000000000000000000000000000003": {
"balance": "0x1"
},
"0000000000000000000000000000000000000004": {
"balance": "0x1"
},
"0000000000000000000000000000000000000005": {
"balance": "0x1"
},
"0000000000000000000000000000000000000006": {
"balance": "0x1"
},
"0000000000000000000000000000000000000007": {
"balance": "0x1"
},
"0000000000000000000000000000000000000008": {
"balance": "0x1"
},
"0000000000000000000000000000000000000009": {
"balance": "0x1"
},
"000000000000000000000000000000000000000a": {
"balance": "0x1"
},
"000000000000000000000000000000000000000b": {
"balance": "0x1"
},
"000000000000000000000000000000000000000c": {
"balance": "0x1"
},
"000000000000000000000000000000000000000d": {
"balance": "0x1"
},
"000000000000000000000000000000000000000e": {
"balance": "0x1"
},
"000000000000000000000000000000000000000f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000010": {
"balance": "0x1"
},
"0000000000000000000000000000000000000011": {
"balance": "0x1"
},
"0000000000000000000000000000000000000012": {
"balance": "0x1"
},
"0000000000000000000000000000000000000013": {
"balance": "0x1"
},
"0000000000000000000000000000000000000014": {
"balance": "0x1"
},
"0000000000000000000000000000000000000015": {
"balance": "0x1"
},
"0000000000000000000000000000000000000016": {
"balance": "0x1"
},
"0000000000000000000000000000000000000017": {
"balance": "0x1"
},
"0000000000000000000000000000000000000018": {
"balance": "0x1"
},
"0000000000000000000000000000000000000019": {
"balance": "0x1"
},
"000000000000000000000000000000000000001a": {
"balance": "0x1"
},
"000000000000000000000000000000000000001b": {
"balance": "0x1"
},
"000000000000000000000000000000000000001c": {
"balance": "0x1"
},
"000000000000000000000000000000000000001d": {
"balance": "0x1"
},
"000000000000000000000000000000000000001e": {
"balance": "0x1"
},
"000000000000000000000000000000000000001f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000020": {
"balance": "0x1"
},
"0000000000000000000000000000000000000021": {
"balance": "0x1"
},
"0000000000000000000000000000000000000022": {
"balance": "0x1"
},
"0000000000000000000000000000000000000023": {
"balance": "0x1"
},
"0000000000000000000000000000000000000024": {
"balance": "0x1"
},
"0000000000000000000000000000000000000025": {
"balance": "0x1"
},
"0000000000000000000000000000000000000026": {
"balance": "0x1"
},
"0000000000000000000000000000000000000027": {
"balance": "0x1"
},
"0000000000000000000000000000000000000028": {
"balance": "0x1"
},
"0000000000000000000000000000000000000029": {
"balance": "0x1"
},
"000000000000000000000000000000000000002a": {
"balance": "0x1"
},
"000000000000000000000000000000000000002b": {
"balance": "0x1"
},
"000000000000000000000000000000000000002c": {
"balance": "0x1"
},
"000000000000000000000000000000000000002d": {
"balance": "0x1"
},
"000000000000000000000000000000000000002e": {
"balance": "0x1"
},
"000000000000000000000000000000000000002f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000030": {
"balance": "0x1"
},
"0000000000000000000000000000000000000031": {
"balance": "0x1"
},
"0000000000000000000000000000000000000032": {
"balance": "0x1"
},
"0000000000000000000000000000000000000033": {
"balance": "0x1"
},
"0000000000000000000000000000000000000034": {
"balance": "0x1"
},
"0000000000000000000000000000000000000035": {
"balance": "0x1"
},
"0000000000000000000000000000000000000036": {
"balance": "0x1"
},
"0000000000000000000000000000000000000037": {
"balance": "0x1"
},
"0000000000000000000000000000000000000038": {
"balance": "0x1"
},
"0000000000000000000000000000000000000039": {
"balance": "0x1"
},
"000000000000000000000000000000000000003a": {
"balance": "0x1"
},
"000000000000000000000000000000000000003b": {
"balance": "0x1"
},
"000000000000000000000000000000000000003c": {
"balance": "0x1"
},
"000000000000000000000000000000000000003d": {
"balance": "0x1"
},
"000000000000000000000000000000000000003e": {
"balance": "0x1"
},
"000000000000000000000000000000000000003f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000040": {
"balance": "0x1"
},
"0000000000000000000000000000000000000041": {
"balance": "0x1"
},
"0000000000000000000000000000000000000042": {
"balance": "0x1"
},
"0000000000000000000000000000000000000043": {
"balance": "0x1"
},
"0000000000000000000000000000000000000044": {
"balance": "0x1"
},
"0000000000000000000000000000000000000045": {
"balance": "0x1"
},
"0000000000000000000000000000000000000046": {
"balance": "0x1"
},
"0000000000000000000000000000000000000047": {
"balance": "0x1"
},
"0000000000000000000000000000000000000048": {
"balance": "0x1"
},
"0000000000000000000000000000000000000049": {
"balance": "0x1"
},
"000000000000000000000000000000000000004a": {
"balance": "0x1"
},
"000000000000000000000000000000000000004b": {
"balance": "0x1"
},
"000000000000000000000000000000000000004c": {
"balance": "0x1"
},
"000000000000000000000000000000000000004d": {
"balance": "0x1"
},
"000000000000000000000000000000000000004e": {
"balance": "0x1"
},
"000000000000000000000000000000000000004f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000050": {
"balance": "0x1"
},
"0000000000000000000000000000000000000051": {
"balance": "0x1"
},
"0000000000000000000000000000000000000052": {
"balance": "0x1"
},
"0000000000000000000000000000000000000053": {
"balance": "0x1"
},
"0000000000000000000000000000000000000054": {
"balance": "0x1"
},
"0000000000000000000000000000000000000055": {
"balance": "0x1"
},
"0000000000000000000000000000000000000056": {
"balance": "0x1"
},
"0000000000000000000000000000000000000057": {
"balance": "0x1"
},
"0000000000000000000000000000000000000058": {
"balance": "0x1"
},
"0000000000000000000000000000000000000059": {
"balance": "0x1"
},
"000000000000000000000000000000000000005a": {
"balance": "0x1"
},
"000000000000000000000000000000000000005b": {
"balance": "0x1"
},
"000000000000000000000000000000000000005c": {
"balance": "0x1"
},
"000000000000000000000000000000000000005d": {
"balance": "0x1"
},
"000000000000000000000000000000000000005e": {
"balance": "0x1"
},
"000000000000000000000000000000000000005f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000060": {
"balance": "0x1"
},
"0000000000000000000000000000000000000061": {
"balance": "0x1"
},
"0000000000000000000000000000000000000062": {
"balance": "0x1"
},
"0000000000000000000000000000000000000063": {
"balance": "0x1"
},
"0000000000000000000000000000000000000064": {
"balance": "0x1"
},
"0000000000000000000000000000000000000065": {
"balance": "0x1"
},
"0000000000000000000000000000000000000066": {
"balance": "0x1"
},
"0000000000000000000000000000000000000067": {
"balance": "0x1"
},
"0000000000000000000000000000000000000068": {
"balance": "0x1"
},
"0000000000000000000000000000000000000069": {
"balance": "0x1"
},
"000000000000000000000000000000000000006a": {
"balance": "0x1"
},
"000000000000000000000000000000000000006b": {
"balance": "0x1"
},
"000000000000000000000000000000000000006c": {
"balance": "0x1"
},
"000000000000000000000000000000000000006d": {
"balance": "0x1"
},
"000000000000000000000000000000000000006e": {
"balance": "0x1"
},
"000000000000000000000000000000000000006f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000070": {
"balance": "0x1"
},
"0000000000000000000000000000000000000071": {
"balance": "0x1"
},
"0000000000000000000000000000000000000072": {
"balance": "0x1"
},
"0000000000000000000000000000000000000073": {
"balance": "0x1"
},
"0000000000000000000000000000000000000074": {
"balance": "0x1"
},
"0000000000000000000000000000000000000075": {
"balance": "0x1"
},
"0000000000000000000000000000000000000076": {
"balance": "0x1"
},
"0000000000000000000000000000000000000077": {
"balance": "0x1"
},
"0000000000000000000000000000000000000078": {
"balance": "0x1"
},
"0000000000000000000000000000000000000079": {
"balance": "0x1"
},
"000000000000000000000000000000000000007a": {
"balance": "0x1"
},
"000000000000000000000000000000000000007b": {
"balance": "0x1"
},
"000000000000000000000000000000000000007c": {
"balance": "0x1"
},
"000000000000000000000000000000000000007d": {
"balance": "0x1"
},
"000000000000000000000000000000000000007e": {
"balance": "0x1"
},
"000000000000000000000000000000000000007f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000080": {
"balance": "0x1"
},
"0000000000000000000000000000000000000081": {
"balance": "0x1"
},
"0000000000000000000000000000000000000082": {
"balance": "0x1"
},
"0000000000000000000000000000000000000083": {
"balance": "0x1"
},
"0000000000000000000000000000000000000084": {
"balance": "0x1"
},
"0000000000000000000000000000000000000085": {
"balance": "0x1"
},
"0000000000000000000000000000000000000086": {
"balance": "0x1"
},
"0000000000000000000000000000000000000087": {
"balance": "0x1"
},
"0000000000000000000000000000000000000088": {
"balance": "0x1"
},
"0000000000000000000000000000000000000089": {
"balance": "0x1"
},
"000000000000000000000000000000000000008a": {
"balance": "0x1"
},
"000000000000000000000000000000000000008b": {
"balance": "0x1"
},
"000000000000000000000000000000000000008c": {
"balance": "0x1"
},
"000000000000000000000000000000000000008d": {
"balance": "0x1"
},
"000000000000000000000000000000000000008e": {
"balance": "0x1"
},
"000000000000000000000000000000000000008f": {
"balance": "0x1"
},
"0000000000000000000000000000000000000090": {
"balance": "0x1"
},
"0000000000000000000000000000000000000091": {
"balance": "0x1"
},
"0000000000000000000000000000000000000092": {
"balance": "0x1"
},
"0000000000000000000000000000000000000093": {
"balance": "0x1"
},
"0000000000000000000000000000000000000094": {
"balance": "0x1"
},
"0000000000000000000000000000000000000095": {
"balance": "0x1"
},
"0000000000000000000000000000000000000096": {
"balance": "0x1"
},
"0000000000000000000000000000000000000097": {
"balance": "0x1"
},
"0000000000000000000000000000000000000098": {
"balance": "0x1"
},
"0000000000000000000000000000000000000099": {
"balance": "0x1"
},
"000000000000000000000000000000000000009a": {
"balance": "0x1"
},
"000000000000000000000000000000000000009b": {
"balance": "0x1"
},
"000000000000000000000000000000000000009c": {
"balance": "0x1"
},
"000000000000000000000000000000000000009d": {
"balance": "0x1"
},
"000000000000000000000000000000000000009e": {
"balance": "0x1"
},
"000000000000000000000000000000000000009f": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a0": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a1": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a2": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a3": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a4": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a5": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a6": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a7": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a8": {
"balance": "0x1"
},
"00000000000000000000000000000000000000a9": {
"balance": "0x1"
},
"00000000000000000000000000000000000000aa": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ab": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ac": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ad": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ae": {
"balance": "0x1"
},
"00000000000000000000000000000000000000af": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b0": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b1": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b2": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b3": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b4": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b5": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b6": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b7": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b8": {
"balance": "0x1"
},
"00000000000000000000000000000000000000b9": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ba": {
"balance": "0x1"
},
"00000000000000000000000000000000000000bb": {
"balance": "0x1"
},
"00000000000000000000000000000000000000bc": {
"balance": "0x1"
},
"00000000000000000000000000000000000000bd": {
"balance": "0x1"
},
"00000000000000000000000000000000000000be": {
"balance": "0x1"
},
"00000000000000000000000000000000000000bf": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c0": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c1": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c2": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c3": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c4": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c5": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c6": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c7": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c8": {
"balance": "0x1"
},
"00000000000000000000000000000000000000c9": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ca": {
"balance": "0x1"
},
"00000000000000000000000000000000000000cb": {
"balance": "0x1"
},
"00000000000000000000000000000000000000cc": {
"balance": "0x1"
},
"00000000000000000000000000000000000000cd": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ce": {
"balance": "0x1"
},
"00000000000000000000000000000000000000cf": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d0": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d1": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d2": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d3": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d4": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d5": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d6": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d7": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d8": {
"balance": "0x1"
},
"00000000000000000000000000000000000000d9": {
"balance": "0x1"
},
"00000000000000000000000000000000000000da": {
"balance": "0x1"
},
"00000000000000000000000000000000000000db": {
"balance": "0x1"
},
"00000000000000000000000000000000000000dc": {
"balance": "0x1"
},
"00000000000000000000000000000000000000dd": {
"balance": "0x1"
},
"00000000000000000000000000000000000000de": {
"balance": "0x1"
},
"00000000000000000000000000000000000000df": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e0": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e1": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e2": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e3": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e4": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e5": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e6": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e7": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e8": {
"balance": "0x1"
},
"00000000000000000000000000000000000000e9": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ea": {
"balance": "0x1"
},
"00000000000000000000000000000000000000eb": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ec": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ed": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ee": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ef": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f0": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f1": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f2": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f3": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f4": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f5": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f6": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f7": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f8": {
"balance": "0x1"
},
"00000000000000000000000000000000000000f9": {
"balance": "0x1"
},
"00000000000000000000000000000000000000fa": {
"balance": "0x1"
},
"00000000000000000000000000000000000000fb": {
"balance": "0x1"
},
"00000000000000000000000000000000000000fc": {
"balance": "0x1"
},
"00000000000000000000000000000000000000fd": {
"balance": "0x1"
},
"00000000000000000000000000000000000000fe": {
"balance": "0x1"
},
"00000000000000000000000000000000000000ff": {
"balance": "0x1"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
Step 3: Maru configuration (follower node)
Cleanup existing data in /opt/maru/data
directory that will be used as
data-path
in Maru Configuration
Create maru/config/maru-config.toml
:
# IMPORTANT: No [qbft] section = follower node (not validator)
# [linea]
# contract-address = "0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5"
# l1-eth-api = { endpoint = "<your Sepolia RPC endpoint>" }
# l1-polling-interval = "6 seconds"
# l1-highest-block-tag = "finalized"
[persistence]
data-path = "/data"
private-key-path = "/data/private-key"
[p2p]
port = 9000 # Default port (can be same as discovery)
ip-address = "0.0.0.0"
static-peers = ["/ip4/3.129.120.128/tcp/31005/p2p/16Uiu2HAmR33t8RZiAHovuH9iH2UuUrajrbfyYowiYDAQo3D5Y9wg", "/ip4/3.129.120.128/tcp/31006/p2p/16Uiu2HAm9HB5oNmnmj8yY6T7dfhLVidVzYqa8QVDtEthkMr6b8tx"]
reconnect-delay = "500ms"
[p2p.discovery]
port = 9000
bootnodes = []
refresh-interval = "3s"
[payload-validator]
engine-api-endpoint = { endpoint = "http://linea-besu:8550" } # Match Besu port!
eth-api-endpoint = { endpoint = "http://linea-besu:8545" }
payload-validation-enabled = false
[observability]
port = 9090
jvm-metrics-enabled = true
prometheus-metrics-enabled = true
[api]
port = 8080
[syncing]
peer-chain-height-polling-interval = "5s"
el-sync-status-refresh-interval = "5s"
sync-target-selection = "Highest"
desync-tolerance = 0
[syncing.download]
block-range-request-timeout = "10s"
blocks-batch-size = 10
blocks-parallelism = 10
max-retries = 5
backoff-delay = "1s"
use-unconditional-random-download-peer = false
Step 4: Maru genesis file
Create maru/config/maru-genesis.json
:
{
"chainId": 59141,
"config": {
"0": {
"type": "difficultyAwareQbft",
"blockTimeSeconds": 2,
"postTtdConfig": {
"validatorSet": ["0x20e2654209c3f5a7b4728fb228778f1261f1013f"],
"elFork": "Paris"
},
"terminalTotalDifficulty": 37331807
},
"1759147200": {
"type": "qbft",
"validatorSet": ["0x20e2654209c3f5a7b4728fb228778f1261f1013f"],
"blockTimeSeconds": 2,
"elFork": "Shanghai"
},
"1759233600": {
"type": "qbft",
"validatorSet": ["0x20e2654209c3f5a7b4728fb228778f1261f1013f"],
"blockTimeSeconds": 2,
"elFork": "Cancun"
},
"1760004000": {
"type": "qbft",
"validatorSet": ["0x20e2654209c3f5a7b4728fb228778f1261f1013f"],
"blockTimeSeconds": 2,
"elFork": "Prague"
}
}
}
Step 5: Create log4j.xml file
Create maru/config/log4j.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="INFO" additivity="false">
<appender-ref ref="console"/>
</Root>
</Loggers>
</Configuration>
Step 6: Docker Compose setup
Create docker-compose.yml
:
networks:
linea: {}
services:
besu:
image: hyperledger/besu:25.8.0
container_name: linea-besu
restart: unless-stopped
networks: [linea]
ports:
- "8545:8545" # JSON-RPC
- "8550:8550" # Engine API
- "30303:30303" # P2P TCP
- "30303:30303/udp" # P2P UDP
environment:
- JAVA_OPTS=-Xmx4g
volumes:
- ./besu/data:/opt/besu/data
- ./besu/config/config.toml:/opt/besu/config.toml:ro
- ./besu/genesis.json:/opt/besu/genesis.json:ro
command:
- --config-file=/opt/besu/config.toml
maru:
image: consensys/maru:54b73d9
container_name: linea-maru
restart: unless-stopped
depends_on:
- besu
networks: [linea]
ports:
- "8080:8080" # Beacon/REST API
- "9000:9000/tcp" # P2P main port
- "9000:9000/udp" # P2P discovery port (UDP only)
- "9090:9090" # Metrics optionnal
environment:
- JAVA_OPTS=-Xmx2g
volumes:
- ./maru/config:/opt/consensys/maru/configs:ro
- ./maru/data:/data
command:
- "java"
- "-Dlog4j2.configurationFile=/opt/consensys/maru/configs/log4j.xml"
- "-jar"
- "/opt/consensys/maru/maru.jar"
- "--maru-genesis-file"
- "/opt/consensys/maru/configs/maru-genesis.json"
- "--config"
- "/opt/consensys/maru/configs/maru-config.toml"
Step 7: Launch and verify
# Start both services (Besu first then Maru)
docker-compose up -d
# Check Besu is syncing
curl -X POST http://localhost:8545 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# Check Maru health
curl -X GET "http://localhost:8080/eth/v1/node/health"
# Verify P2P connection
docker logs linea-maru | grep "Currently connected peers"
# Should show: peers=[16Uiu2HAmR33t8RZiAHovuH9iH2UuUrajrbfyYowiYDAQo3D5Y9wg]
JWT Authentication
For production environments, you should enable JWT authentication:
Generate JWT secret
openssl rand -hex 32 > ~/linea-node/jwt/jwt.hex
Update Besu config
# Replace engine-jwt-disabled=true with:
engine-jwt-enabled=true
engine-jwt-file="/opt/besu/jwt.hex"
Update Maru config
[payload-validator]
engine-api-endpoint = { endpoint = "http://linea-besu:8550", jwt-secret-path = "/jwt.hex" }
Update Docker volumes
# Add to both Besu and Maru:
volumes:
- ./jwt/jwt.hex:/jwt.hex:ro
Private key management
Development: Maru auto-generates private keys at startup if none exist. (TBC)
Production: Generate and backup your private key (not mandatory):
# Option 1: Let Maru auto-generate, then backup:
docker cp linea-maru:/data/private-key ./backup/
# Option 2: Generate using Maru's key generation tool:
# https://github.com/Consensys/maru/tree/main/jvm-libs/utils
Maintaining the same private key preserves node identity across restarts.
Troubleshooting
Debug commands
# EL client block height
curl -s -X POST http://localhost:8545 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# Maru health
curl http://localhost:8080/eth/v1/node/health
# Check block headers
curl http://localhost:8080/eth/v1/beacon/headers/head