Skip to main content

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

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
Important

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