Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.optimism.io/llms.txt

Use this file to discover all available pages before exploring further.

op-geth reaches end-of-support on 2026-05-31 and will not support the L1 Glamsterdam hardfork. This tutorial builds and runs op-reth (the primary supported execution client) and Nethermind. op-geth instructions are retained in a legacy section at the bottom for operators mid-migration. See the op-geth deprecation notice for the full migration plan.
This tutorial walks through the full process of building and running an OP Stack node from source — op-node plus an execution client. Building from source is a flexible alternative to using pre-built Docker images and is useful if you need a specific architecture or want to inspect what you’re running. For permissionless chains setting up historical proofs for withdrawal proving, follow this tutorial first to get a working node, then continue with Running op-reth with Historical Proofs.

Hardware requirements

Hardware requirements for OP Mainnet nodes can vary depending on the type of node you plan to run. Archive nodes generally require significantly more resources than full nodes. Below are suggested minimum hardware requirements for each type of node.
  • 16GB RAM
  • Reasonably modern CPU

SSD capacity requirements

Given the growing size of the blockchain state, choosing the right SSD size is important. Below are the storage needs as of June 2025:
  • Full Node: The snapshot size for a full node is approximately 700GB, with the data directory’s capacity increasing by about 100GB every six months.
  • Archive Node: The snapshot size for an archive node is approximately 14TB, with the data directory’s capacity increasing by about 3.5TB every six months. A local SSD with a NVME interface is recommended for archive nodes.
Plan for future storage needs and choose SSDs that can handle these increasing requirements.

Software dependencies

The build environment is managed through mise, which installs and manages the toolchains (Rust, Go) needed to build the monorepo. The mise configuration lives in mise.toml at the monorepo root.

Build op-node and the execution client

The Optimism Monorepo does coordinated releases — each release commit is tagged simultaneously across components (op-node, op-reth, kona-node, etc.). Pinning to one release tag gives you a known-good combination of all of them built from the same source state. Look up the latest tags on the Optimism releases page.
1

Clone the Optimism Monorepo

The monorepo contains the source for both op-node and op-reth.
git clone https://github.com/ethereum-optimism/optimism.git
cd optimism
2

Pin to a release commit

Check out a recent op-node release tag — the same commit carries the matching op-reth tag. For example, op-node/v1.18.2 is co-tagged with op-reth/v2.2.4:
git checkout op-node/v1.18.2
op-reth/v2.2.3 or later is required to enable the historical proof store v2 (--proofs-history.storage-version=v2). The op-node/v1.18.x release line carries op-reth ≥ v2.2.3, so any of those tags satisfies that requirement.
3

Build op-node

cd op-node && just && cd ..
Binary: op-node/bin/op-node (relative to the monorepo root).
4

Build the execution client

op-reth lives inside the monorepo at rust/op-reth/ — no second git clone needed.
cd rust/op-reth && cargo build --release --bin op-reth && cd ../..
Binary: rust/target/release/op-reth (relative to the monorepo root). rust/ is a Cargo workspace, so the target/ directory lives at the workspace root, not inside the individual crate. Build outputs are gitignored, so they persist across any future git checkout.

Assess blob archiver

Assess if you need to configure a blob archiver service by reading Configure a Blob Archiver.

Create a JWT secret

The execution client and op-node communicate over the engine API authrpc, secured with a shared 32-byte hex secret. Both binaries must read the same secret file. The commands below use a relative path (./jwt.txt) for each binary, so the same content must exist at both launch directories.
1

Generate the secret

From the monorepo root:
openssl rand -hex 32 > jwt.txt
This creates optimism/jwt.txt.
2

Copy to the op-node launch directory

cp jwt.txt op-node/jwt.txt
3

Copy to the op-reth launch directory

The op-reth binary lives at rust/target/release/op-reth and the tutorial launches it from rust/, so place the JWT there:
cp jwt.txt rust/jwt.txt
The two copies must remain identical — if you regenerate one side, copy it over to the other before restarting.

Start the execution client

It’s generally easier to start the execution client before op-node. The EL will simply not receive any blocks until op-node is started.
1

Navigate to the rust workspace directory

op-reth is built into the workspace target dir, so launch from optimism/rust/:
cd /path/to/optimism/rust
The binary is then at ./target/release/op-reth.
2

Set environment variables

export DATADIR_PATH=... # Path to the desired data directory for op-reth
3

Start op-reth

For archive-node configuration (historical state for withdrawal proving), see the OP Mainnet archive nodes section.
The JSON-RPC API will become available on port 8545. See the op-reth configuration reference for the full flag set.
./target/release/op-reth node \
  --chain=optimism_sepolia \
  --datadir=$DATADIR_PATH \
  --http \
  --ws \
  --authrpc.jwtsecret=./jwt.txt \
  --rollup.sequencer=https://sepolia-sequencer.optimism.io
For OP Mainnet, set --chain=optimism and --rollup.sequencer=https://mainnet-sequencer.optimism.io.

Start op-node

Once your execution client is running, start op-node. It will connect to the EL and begin synchronizing the chain.
1

Navigate to the op-node directory

op-node is built into its own crate directory, so launch from optimism/op-node/:
cd /path/to/optimism/op-node
The binary is then at ./bin/op-node.
2

Set environment variables

export L1_RPC_URL=...  # URL for the L1 node. Local default: http://127.0.0.1:8545
export L1_RPC_KIND=... # alchemy, quicknode, infura, parity, nethermind, debug_geth, erigon, basic, any
export L1_BEACON_URL=... # URL for the L1 Beacon HTTP endpoint. Local default: http://127.0.0.1:3500
3

Start op-node

The op-node RPC should not be exposed publicly. If left exposed, it could accidentally expose admin controls to the public internet.
--syncmode=execution-layer enables snap sync, which works for both op-reth and Nethermind and removes the need to initialize the node with a data directory.
--l2.enginekind=reth is required when pairing with op-reth. The binary still defaults to geth for backward compatibility — set --l2.enginekind=geth if you are still running op-geth.
./bin/op-node \
  --l1=$L1_RPC_URL \
  --l1.rpckind=$L1_RPC_KIND \
  --l1.beacon=$L1_BEACON_URL \
  --l2=ws://localhost:8551 \
  --l2.jwt-secret=./jwt.txt \
  --network=op-sepolia \
  --syncmode=execution-layer \
  --l2.enginekind=reth
Some L1 nodes (e.g. Erigon) do not support eth_getProof. Add --l1.trustrpc if your L1 doesn’t support it — this means op-node trusts the L1 node to provide correct data.

Synchronization verification

Once the EL and op-node are running, you should see them begin to communicate and synchronize.

Snap sync (default)

Initial synchronization can take several hours. At the start of snap sync, op-node will log:
INFO [03-06|10:56:55.602] Starting EL sync
INFO [03-06|10:56:55.615] Sync progress                            reason="unsafe payload from sequencer while in EL sync" l2_finalized=000000..000000:0 l2_safe=000000..000000:0 l2_pending_safe=000000..000000:0 l2_unsafe=4284ab..7e7e84:117076319 l2_time=1,709,751,415 l1_derived=000000..000000:0
INFO [03-06|10:56:57.567] Optimistically inserting unsafe L2 execution payload to drive EL sync id=4ac160..df4d12:117076320
Starting EL sync is shown once and the sync-progress / inserting logs repeat until done. op-node will log the following when finished:
lvl=info msg="Finished EL sync" sync_duration=23h25m0.370558429s finalized_block=0x4f69e83ff1407f2e2882f2526ee8a154ac326590799889cede3af04a7742f18d:116817417
The execution client logs its own header- and state-download progress in parallel:
op-reth logs sync stages (headers, bodies, execution, state root) with periodic progress lines. Monitor the op-reth logs to confirm it is advancing through stages alongside the op-node Sync progress lines.

Full sync

Full sync rebuilds the chain from genesis and can take days to weeks on mature networks. Most operators should use snap sync (above) or bootstrap from a pre-synced snapshot. Full-sync configuration is client-specific:
op-reth’s snap sync is the recommended initialization path. For alternative sync configurations, see the op-reth configuration reference.
After the initial sync, op-node derives L1 batches into L2 blocks and feeds them to the execution client. You’ll see logs like:
INFO [06-26|13:31:20.389] Advancing bq origin                      origin=17171d..1bc69b:8300332 originBehind=false
INFO [06-26|14:00:59.460] Sync progress                            reason="processed safe block derived from L1" l2_safe=7fe3f6..900127:4068014 l2_unsafe=7fe3f6..900127:4068014 l1_derived=6079cd..be4231:8301091
INFO [06-26|14:00:59.461] generated attributes in payload queue    txs=1  timestamp=1,673,564,098
INFO [06-26|14:00:59.463] inserted block                           hash=e80dc4..72a759 number=4,068,015 update_safe=true
The execution client logs its own block-import progress in parallel; refer to its documentation for log specifics.

OP Mainnet archive nodes

You only need an archive node if you need historical state. Most node operators should default to full nodes.
For op-reth, historical state for withdrawal proving is configured via either --rpc.eth-proof-window (no separate proofs database; size to your dispute-game cadence) or --proofs-history (bounded memory, larger storage). See:

Legacy Geth (pre-Bedrock, optional)

Blocks and transactions included in OP Mainnet before the Bedrock Upgrade cannot be executed by modern OP Mainnet nodes. Modern nodes serve these blocks but cannot run stateful queries like eth_call against them. For complete archive coverage of pre-Bedrock OP Mainnet state, run a Legacy Geth (l2geth) node alongside your modern node. This is only relevant to OP Mainnet archive nodes; skip if running a full node or OP Sepolia.
1

Build l2geth

git clone https://github.com/ethereum-optimism/optimism-legacy.git
cd optimism-legacy/l2geth
make
2

Download the legacy Geth data directory

Download the snapshot from Legacy Geth Data Directory (2.9TB) and verify:
sha256sum mainnet-legacy-archival.tar.zst
# Expected: 4adedb61125b81b55f9bdccc2e85092050c65ef2253c86e2b79569732b772829
Then extract:
tar xvf mainnet-legacy-archival.tar.zst
3

Start l2geth

USING_OVM=true \
  ETH1_SYNC_SERVICE_ENABLE=false \
  RPC_API=eth,rollup,net,web3,debug \
  RPC_ENABLE=true \
  RPC_PORT=8546 \
  ./build/bin/geth --datadir /path/to/l2geth-datadir

op-geth (legacy — end of support 2026-05-31)

op-geth support ends on 2026-05-31 and will not support the Glamsterdam hardfork. For new deployments, use op-reth (above). See the op-geth deprecation notice for the migration plan.
Pair op-geth with op-node by setting --l2.enginekind=geth (the binary default) instead of reth in the op-node command above.

Build op-geth

1

Clone op-geth

git clone https://github.com/ethereum-optimism/op-geth.git
cd op-geth
2

Check out the required release branch

Check the op-geth releases page for the correct branch.
git checkout <name of release branch>
3

Build op-geth

make geth

Start op-geth

For an archive node, also set --gcmode=archive.
./build/bin/geth \
  --http \
  --http.port=8545 \
  --http.addr=localhost \
  --authrpc.addr=localhost \
  --authrpc.jwtsecret=./jwt.txt \
  --verbosity=3 \
  --rollup.sequencerhttp=https://sepolia-sequencer.optimism.io/ \
  --op-network=op-sepolia \
  --datadir=$DATADIR_PATH
See the op-geth configuration reference for the full flag set.

op-geth snap-sync stages

op-geth’s snap sync runs in two stages — header download then state download:
lvl=info msg="Syncing beacon headers" downloaded=116775778 left=1162878 eta=53.182s
lvl=info msg="Syncing: state download in progress" synced=99.75% state="191.33 GiB" accounts=124,983,[email protected] slots=806,829,[email protected] [email protected] eta=-2m7.602s
msg="Syncing: chain download in progress" synced=100.00% chain="176.01 GiB" headers=116,817,[email protected] bodies=116,817,[email protected] receipts=116,817,[email protected] eta=77.430ms
Once synced, op-geth logs block imports as op-node feeds payloads:
INFO [06-26|14:02:12.974] Imported new potential chain segment     number=4,068,194 hash=a334a0..609a83 blocks=1 txs=1
INFO [06-26|14:02:12.976] Chain head was updated                   number=4,068,194 hash=a334a0..609a83

Next steps