Operators
Spinning up the sequencer

Overview

This guide provides step-by-step instructions for spinning up a sequencer node after deploying L1 smart contracts for your OP Stack chain with op-deployer.

A sequencer node consists of two core components:

  • op-geth: Execution layer that processes transactions and maintains state
  • op-node: Consensus layer that orders transactions and creates L2 blocks

The sequencer is responsible for:

  • Ordering transactions from users
  • Building L2 blocks
  • Signing blocks on the P2P network

Prerequisites

Essential requirements

Before spinning up your sequencer, complete the following steps:

1. Successful L1 contract deployment:

  • Deployed L1 contracts using op-deployer apply command.
  • Generated genesis and rollup configuration files using:
    op-deployer inspect genesis --workdir .deployer <L2_CHAIN_ID> > .deployer/genesis.json
    op-deployer inspect rollup --workdir .deployer <L2_CHAIN_ID> > .deployer/rollup.json

2. Required configuration files:

  • genesis.json - L2 genesis file for initializing op-geth
  • rollup.json - Rollup configuration file for op-node
  • Access to your deployment state.json file from op-deployer

3. L1 network access:

  • L1 RPC endpoint (Ethereum, Sepolia, etc.)
  • L1 Beacon node endpoint

Software requirements

  • Git (for cloning repositories)
  • Go 1.21+ (if building from source)
  • Docker and Docker Compose (optional but recommended)
  • OpenSSL for JWT secret generation

Finding the current stable releases

To ensure you're using the latest compatible versions of OP Stack components, always check the official releases page:

release page (opens in a new tab)

The main components you'll need for sequencer deployment are:

The versions used in this guide (op-node/v1.13.3 and op-geth/v1.101511.0) are verified compatible versions.

According to the op-node v1.13.3 release notes (opens in a new tab), this op-node version specifically corresponds to op-geth v1.101511.0. Always check the release notes to ensure you're using compatible versions.

Software installation

For spinning up a sequencer, we recommend building from source as it provides better control, and helps with debugging. In this guide, a Docker alternative is also provided.

Build from source (Recommended)

Building from source gives you full control over the binaries and is the preferred approach for this guide.

1. Clone and build op-node

# Clone the optimism monorepo
git clone https://github.com/ethereum-optimism/optimism.git
cd optimism
 
# Checkout the latest release tag
git checkout op-node/v1.13.3
 
# Build op-node
cd op-node
just
 
# Binary will be available at ./bin/op-node

2. Clone and build op-geth

# Clone op-geth repository (in a separate directory)
git clone https://github.com/ethereum-optimism/op-geth.git
cd op-geth
 
# Checkout to this release tag
git checkout v1.101511.0
 
# Build op-geth
make geth
 
# Binary will be available at ./build/bin/geth

Verify installation

Check that you have properly installed the needed components.

# Make sure you're in the right directory
./bin/op-node --version
./build/bin/geth version

Configuration setup

1. Organize your workspace

After building the binaries, you should have the following directory structure:

~/
├── optimism/               # Optimism monorepo
│   └── op-node/
│       └── bin/
│           └── op-node     # Built binary
├── op-geth/               # OP-Geth repository  
│   └── build/
│       └── bin/
│           └── geth       # Built binary
└── .deployer/             # From op-deployer
    ├── genesis.json
    └── rollup.json

Now create your sequencer working directory. We recommend creating it at the same level for easy path references:

# Create sequencer directory at the root level
mkdir ~/sequencer-node
cd ~/sequencer-node
Alternative: Copy binaries to sequencer directory

If you prefer to keep binaries close to your configuration, you can copy them:

mkdir ~/sequencer-node/bin
cp ~/optimism/op-node/bin/op-node ~/sequencer-node/bin/
cp ~/op-geth/build/bin/geth ~/sequencer-node/bin/
 
# Then update scripts to use:
# ./bin/geth   
# ./bin/op-node

2. Generate JWT secret

# Generate JWT secret in the sequencer directory
openssl rand -hex 32 > jwt.txt
 
# Set appropriate permissions
chmod 600 jwt.txt

3. Set up directory structure and copy files

In this guide, we're going to be using the binaries from their original build locations, we only need to create directories for configuration files and scripts.

# Create scripts directory
mkdir scripts
 
# Copy configuration files from op-deployer
cp ~/.deployer/genesis.json .
cp ~/.deployer/rollup.json .

Your final directory structure should look like:

~/sequencer-node/
├── jwt.txt
├── genesis.json
├── rollup.json
└── scripts/
    ├── start-op-geth.sh    # (to be created)
    └── start-op-node.sh    # (to be created)

4. Environment variables

You'll need to gather several pieces of information before creating your configuration. Here's where to get each value:

Get L1 network access

You need access to the L1 network (Ethereum mainnet or Sepolia testnet) and its beacon node:

L1 RPC URL options:

L1 Beacon URL options:

  • Public beacon APIs: https://ethereum-sepolia-beacon-api.publicnode.com (Sepolia) or https://ethereum-beacon-api.publicnode.com (Mainnet)
  • Infura beacon: https://sepolia.infura.io/v3/YOUR_KEY (if your Infura plan includes beacon access)

Get private key from your wallet

For this basic sequencer setup, you only need a private key during op-node initialization.

Get your public IP address

# Find your public IP address, once you get it, update the P2P_ADVERTISE_IP in your .env
curl ifconfig.me
# or
curl ipinfo.io/ip

Choose your ports

The default ports are standard but can be changed if needed:

  • 8545: op-geth HTTP RPC (standard Ethereum RPC port)
  • 8546: op-geth WebSocket RPC
  • 8551: op-geth Auth RPC (for op-node communication)
  • 8547: op-node RPC
  • 9222: P2P networking (must be open on firewall)

Now create your .env file with the actual values:

# Create .env file with your actual values
# L1 Configuration - Replace with your actual RPC URLs
L1_RPC_URL=https://sepolia.infura.io/v3/YOUR_ACTUAL_INFURA_KEY
L1_BEACON_URL=https://ethereum-sepolia-beacon-api.publicnode.com
 
# Sequencer configuration
SEQUENCER_ENABLED=true
SEQUENCER_STOPPED=false
 
# Private keys
PRIVATE_KEY=0xYOUR_ACTUAL_PRIVATE_KEY
 
# P2P configuration - Replace with your actual public IP
P2P_LISTEN_PORT=9222
P2P_ADVERTISE_IP=YOUR_ACTUAL_PUBLIC_IP
 
# RPC configuration (can customize ports if needed)
OP_NODE_RPC_PORT=8547
OP_GETH_HTTP_PORT=8545
OP_GETH_WS_PORT=8546
OP_GETH_AUTH_PORT=8551
 
# JWT secret location
JWT_SECRET=./jwt.txt

Important: Replace ALL placeholder values (YOUR_ACTUAL_*) with your real configuration values.

Sequencer specific configuration

op-geth configuration for sequencer

Create scripts/start-op-geth.sh:

#!/bin/bash
source .env
 
# Path to the op-geth binary we built
../op-geth/build/bin/geth \
  --datadir=./op-geth-data \
  --http \
  --http.addr=0.0.0.0 \
  --http.port=$OP_GETH_HTTP_PORT \
  --http.vhosts="*" \
  --http.corsdomain="*" \
  --http.api=eth,net,web3,debug,txpool,admin \
  --ws \
  --ws.addr=0.0.0.0 \
  --ws.port=$OP_GETH_WS_PORT \
  --ws.origins="*" \
  --ws.api=eth,net,web3,debug,txpool,admin \
  --authrpc.addr=0.0.0.0 \
  --authrpc.port=$OP_GETH_AUTH_PORT \
  --authrpc.vhosts="*" \
  --authrpc.jwtsecret=$JWT_SECRET \
  --syncmode=full \
  --gcmode=archive \
  --rollup.disabletxpoolgossip=true \
  --rollup.sequencerhttp=http://localhost:$OP_NODE_RPC_PORT

op-node configuration for sequencer

Create scripts/start-op-node.sh:

#!/bin/bash
 
source .env
 
# Path to the op-node binary we built
../optimism/op-node/bin/op-node \
  --l1=$L1_RPC_URL \
  --l1.beacon=$L1_BEACON_URL \
  --l2=http://localhost:$OP_GETH_AUTH_PORT \
  --l2.jwt-secret=$JWT_SECRET \
  --rollup.config=./rollup.json \
  --sequencer.enabled=$SEQUENCER_ENABLED \
  --sequencer.stopped=$SEQUENCER_STOPPED \
  --sequencer.max-safe-lag=3600 \
  --verifier.l1-confs=4 \
  --p2p.listen.ip=0.0.0.0 \
  --p2p.listen.tcp=$P2P_LISTEN_PORT \
  --p2p.listen.udp=$P2P_LISTEN_PORT \
  --p2p.advertise.ip=$P2P_ADVERTISE_IP \
  --p2p.advertise.tcp=$P2P_LISTEN_PORT \
  --p2p.advertise.udp=$P2P_LISTEN_PORT \
  --p2p.sequencer.key=$PRIVATE_KEY \
  --rpc.addr=0.0.0.0 \
  --rpc.port=$OP_NODE_RPC_PORT \
  --rpc.enable-admin \
  --log.level=info \
  --log.format=json

Initializing and starting the sequencer

Initialize op-geth with your genesis file

# Make sure you're in the sequencer-node directory
cd ~/sequencer-node
 
# Initialize op-geth with your genesis file
../op-geth/build/bin/geth init --datadir=./op-geth-data --state.scheme=hash ./genesis.json

Start op-geth

# Make scripts executable
chmod +x scripts/start-op-geth.sh
chmod +x scripts/start-op-node.sh
 
# Start op-geth in the background or in a separate terminal
./scripts/start-op-geth.sh

Note: You should see output indicating that op-geth is starting and listening on the configured ports.

Start op-node

# In a separate terminal, navigate to the sequencer directory
cd ~/sequencer-node
 
# Start op-node
./scripts/start-op-node.sh

Verify sequencer is running

Once both services are running, verify they're working correctly:

# Check op-geth is responding, do this in another terminal
curl -X POST -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
  http://localhost:8545
 
# Check sequencer status
curl -X POST -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","method":"admin_sequencerActive","params":[],"id":1}' \
  http://localhost:8547

Your sequencer node is now operational and ready to process transactions.

Docker alternative (For containerized environments)

If you prefer containerized deployment, you can use the official Docker images.

Complete Docker setup guide

Complete Docker setup guide

If you choose the Docker approach, you'll need to:

  1. Set up directory structure and copy configuration files:
# Create your sequencer working directory
mkdir ~/sequencer-node
cd ~/sequencer-node
 
# Copy configuration files from op-deployer output
# Note: Adjust the path if your .deployer directory is located elsewhere
cp ~/.deployer/genesis.json .
cp ~/.deployer/rollup.json .
 
# Generate JWT secret
openssl rand -hex 32 > jwt.txt
chmod 600 jwt.txt
  1. Create environment variables file:
# Create .env file with your actual values
cat > .env << 'EOF'
# L1 Configuration - Replace with your actual RPC URLs
L1_RPC_URL=https://sepolia.infura.io/v3/YOUR_ACTUAL_INFURA_KEY
L1_BEACON_URL=https://ethereum-sepolia-beacon-api.publicnode.com
 
# Private keys - Replace with your actual private key
PRIVATE_KEY=0xYOUR_ACTUAL_PRIVATE_KEY
 
# (Run `curl ifconfig.me` in a separate shell to obtain the value, then paste it below)
 
# P2P configuration - Replace with your actual public IP
P2P_ADVERTISE_IP=YOUR_ACTUAL_PUBLIC_IP
 
EOF

Important: Replace ALL placeholder values (YOUR_ACTUAL_*) with your real configuration values.

  1. Create docker-compose.yml:
version: '3.8'
 
services:
op-geth:
  image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101511.0
  volumes:
    # Mount entire directory to avoid file mounting issues
    - .:/workspace
  working_dir: /workspace
  ports:
    - "8545:8545"
    - "8546:8546"
    - "8551:8551"
  command:
    - "--datadir=/workspace/op-geth-data"
    - "--http"
    - "--http.addr=0.0.0.0"
    - "--http.port=8545"
    - "--ws"
    - "--ws.addr=0.0.0.0"
    - "--ws.port=8546"
    - "--authrpc.addr=0.0.0.0"
    - "--authrpc.port=8551"
    - "--authrpc.jwtsecret=/workspace/jwt.txt"
    - "--syncmode=full"
    - "--gcmode=archive"
    - "--rollup.disabletxpoolgossip=true"
    - "--rollup.sequencerhttp=http://op-node:8547"
    - "--http.vhosts=*"
    - "--http.corsdomain=*"
    - "--http.api=eth,net,web3,debug,txpool,admin"
    - "--ws.origins=*"
    - "--ws.api=eth,net,web3,debug,txpool,admin"
    - "--authrpc.vhosts=*"
 
op-node:
  image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:v1.13.3
  depends_on:
    - op-geth
  volumes:
    - .:/workspace
  working_dir: /workspace
  ports:
    - "8547:8547"
    - "9222:9222"
  environment:
    - L1_RPC_URL=${L1_RPC_URL}
    - L1_BEACON_URL=${L1_BEACON_URL}
    - PRIVATE_KEY=${PRIVATE_KEY}
    - P2P_ADVERTISE_IP=${P2P_ADVERTISE_IP}
  command:
    - "op-node"
    - "--l1=${L1_RPC_URL}"
    - "--l1.beacon=${L1_BEACON_URL}"
    - "--l2=http://op-geth:8551"
    - "--l2.jwt-secret=/workspace/jwt.txt"
    - "--rollup.config=/workspace/rollup.json"
    - "--sequencer.enabled=true"
    - "--sequencer.stopped=false"
    - "--sequencer.max-safe-lag=3600"
    - "--verifier.l1-confs=4"
    - "--p2p.listen.ip=0.0.0.0"
    - "--p2p.listen.tcp=9222"
    - "--p2p.listen.udp=9222"
    - "--p2p.advertise.ip=${P2P_ADVERTISE_IP}"
    - "--p2p.advertise.tcp=9222"
    - "--p2p.advertise.udp=9222"
    - "--p2p.sequencer.key=${PRIVATE_KEY}"
    - "--rpc.addr=0.0.0.0"
    - "--rpc.port=8547"
    - "--rpc.enable-admin"
    - "--log.level=info"
    - "--log.format=json"
  1. Initialize op-geth with Docker:
# Make sure you're in the sequencer-node directory with all files copied
cd ~/sequencer-node
 
# Initialize op-geth using Docker
docker run --rm \
  -v $(pwd):/workspace \
  -w /workspace \
  us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101511.0 \
  init --datadir=./op-geth-data --state.scheme=hash ./genesis.json
  1. Start the services:
# Start both services
docker-compose up -d
 
# View logs
docker-compose logs -f
  1. Final directory structure:
~/sequencer-node/
├── jwt.txt                 # Generated JWT secret
├── genesis.json           # Copied from ~/.deployer/
├── rollup.json            # Copied from ~/.deployer/
├── .env                   # Environment variables
├── docker-compose.yml     # Docker configuration
└── op-geth-data/         # Created by Docker during initialization
    ├── geth/             # Geth data
    └── keystore/         # Key files

Your sequencer node is now operational and ready to process transactions.

Next steps