Running a testnet or mainnet node


If you're looking to build an app on Optimism you'll need access to an Optimism node. You can get a hosted one from:

However, you might be interested in running your very own Optimism node. Here we'll go over the process of running a testnet or mainnet Optimism node for yourself.

# Upgrades

If you run a node you need to subscribe to an update feed (either the mailing list (opens new window) or the RSS feed (opens new window)) to know when to upgrade. Otherwise, your node will eventually stop working.

# Hardware requirements

Replicas need to store the transaction history of Optimism and to run Geth. They need to be relatively powerful machines (real or virtual). We recommend at least 16 GB RAM, and an SSD drive with at least 100 GB free.

# Docker configuration

The recommended method to create a replica is to use Docker (opens new window) and the Docker images we provide (opens new window). They include all the configuration settings. This is the recommended method because it is what we for our own systems. As such, the docker images go through a lot more tests than any other configuration.

You'll need to have the following installed:

  1. Docker (opens new window)
  2. Docker compose (opens new window)

# Configuring and running the node

Follow the instructions available at the monorepo (opens new window) to build and run the node.

# Non-docker configuration

Here are the instructions if you want to build you own replica without relying on our images. These instructions were generated with a GCP e2-standard-4 (opens new window) virtual machine running Debian 10 (opens new window) with a 100 GB SSD drive. They should work on different operating systems with minor changes, but there are no guarantees.

Note that these directions are for a replica of the main network. You need to modify some of them if you want to create a replica of the Kovan test network.

Note: This is not the recommended configuration. While we did QA on these instructions and they work, the QA that the docker images undergo is much more extensive.

# Install packages

  1. These packages are all required either to compile the software or to run it. We need libusb-1.0 because geth requires it to check for hardware wallets.

    sudo apt install -y git make wget gcc pkg-config libusb-1.0 jq
    
    1
  2. Install the node.js package (opens new window). These instructions were written using the 12.x version.

  3. Install yarn (opens new window):

     sudo npm install -g yarn 
    
    1
  4. Install the Go programming language (opens new window). These instructions were written using Go version 1.17.6

# The Data Transport Layer (DTL)

This TypeScript program reads data from an Optimism endpoint and passes it over to the local instance of l2geth (geth (opens new window) with minor changes for layer 2 support).

  1. Download the source code (opens new window). Then, compile the DTL:

    git clone -b master https://github.com/ethereum-optimism/optimism.git     
    cd optimism
    yarn
    yarn build
    cd ~/optimism/packages/data-transport-layer
    cp .env.example .env
    
    1
    2
    3
    4
    5
    6
  2. Edit .env to specify your own configuration. Modify these parameters:

    Parameter Value
    DATA_TRANSPORT_LAYER__NODE_ENV production
    DATA_TRANSPORT_LAYER__ETH_NETWORK_NAME mainnet
    DATA_TRANSPORT_LAYER__ADDRESS_MANAGER 0xdE1FCfB0851916CA5101820A69b13a4E276bd81F
    DATA_TRANSPORT_LAYER__SERVER_HOSTNAME localhost
    DATA_TRANSPORT_LAYER__SERVER_PORT 7878
    DATA_TRANSPORT_LAYER__SYNC_FROM_L1 false
    DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT Get an endpoint from a service provider (opens new window) unless you run a node yourself
    DATA_TRANSPORT_LAYER__SYNC_FROM_L2 true
    DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT See here
    DATA_TRANSPORT_LAYER__L2_CHAIN_ID 10 (for a mainnet replica)

    These directions are written with the assumption that you sync from L2, which is faster. If you prefer, you can syncronize from L1, which is more secure but slower. To use L1, keep the value of DATA_TRANSPORT_LAYER__SYNC_FROM_L1 as true and DATA_TRANSPORT_LAYER__SYNC_FROM_L2 as false. Also, add this line:

    DATA_TRANSPORT_LAYER__L1_START_HEIGHT=13596466
    
    1
  3. Start the DTL (as a daemon, logging to ~/dtl.log):

    nohup yarn start > ~/dtl.log &
    
    1

    Note that you cannot just close the window if you want DTL to continue running, you have to exit the shell gracefully.

  4. To verify the DTL is running correctly you can run a command.

    • If synchronizing from L2:

      curl -s http://localhost:7878/eth/syncing?backend=l2  | jq .currentTransactionIndex
      
      1
    • If synchronizing from L1:

      curl -s http://localhost:7878/eth/syncing?backend=l1  | jq .currentTransactionIndex
      
      1

    It gives you the current transaction index, which should increase with time.

  5. For debugging purposes, it is sometimes useful to get a transaction's information from the DTL:

    curl -s http://localhost:7878/transaction/index/<transaction number>?backend=l2 | jq .transaction
    
    1

    Note that the transaction indexes are one below the number on etherscan, so for example

    curl -s http://localhost:7878/transaction/index/31337?backend=l2 | jq .transaction
    
    1

    Corresponds to Etherscan transaction 31338 (opens new window).

The DTL now needs to download the entire transaction history since regenesis, a process that takes hours. While it is running, we can get started on the client software.

# The Optimism client software

The client software, called l2geth, is a minimally modified version of geth (opens new window). Because geth supports hardware wallets you might get USB errors. If you do, ignore them.

These directions use ~/gethData as the data directory. You can replace it with you own directory as long as you are consistent.

  1. To compile l2geth, run:

    cd ~/optimism/l2geth
    make geth
    
    1
    2
  2. Download and verify the genesis state, the state of the Optimism blockchain during the final regenesis, 11 November 2021.

    wget -O /tmp/genesis.json https://storage.googleapis.com/optimism/mainnet/genesis-berlin.json
    sha256sum /tmp/genesis.json
    
    1
    2

    The output of the sha256sum command should be:

    0x106b0a3247ca54714381b1109e82cc6b7e32fd79ae56fbcc2e7b1541122f84ea  /tmp/genesis.json
    
    1
  3. Create a file called env.sh (in whatever directory is convenient) with this content:

    export CHAIN_ID=10
    export DATADIR=~/gethData
    export NETWORK_ID=10
    export NO_DISCOVER=true
    export NO_USB=true
    export GASPRICE=0
    export GCMODE=archive
    export BLOCK_SIGNER_ADDRESS=0x00000398232E2064F896018496b4b44b3D62751F
    export BLOCK_SIGNER_PRIVATE_KEY=6587ae678cf4fc9a33000cdbf9f35226b71dcc6a4684a31203241f9bcfd55d27
    export ETH1_CTC_DEPLOYMENT_HEIGHT=13596466
    export ETH1_SYNC_SERVICE_ENABLE=true
    export ROLLUP_ADDRESS_MANAGER_OWNER_ADDRESS=0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A
    export ROLLUP_CLIENT_HTTP=http://localhost:7878
    export ROLLUP_DISABLE_TRANSFERS=false
    export ROLLUP_ENABLE_L2_GAS_POLLING=false
    export ROLLUP_GAS_PRICE_ORACLE_OWNER_ADDRESS=0x648E3e8101BFaB7bf5997Bd007Fb473786019159
    export ROLLUP_MAX_CALLDATA_SIZE=40000
    export ROLLUP_POLL_INTERVAL_FLAG=1s
    export ROLLUP_SYNC_SERVICE_ENABLE=true
    export ROLLUP_TIMESTAMP_REFRESH=5m
    export ROLLUP_VERIFIER_ENABLE=true
    export RPC_ADDR=0.0.0.0
    export RPC_API=eth,rollup,net,web3,debug
    export RPC_CORS_DOMAIN=*
    export RPC_ENABLE=true
    export RPC_PORT=8545
    export RPC_VHOSTS=*
    export TARGET_GAS_LIMIT=15000000
    export USING_OVM=true
    export WS_ADDR=0.0.0.0
    export WS_API=eth,rollup,net,web3,debug
    export WS_ORIGINS=*
    export WS=true
    export ROLLUP_BACKEND=l2    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34

    Note: If synchronizing from L1, replace the last line with

    export ROLLUP_BACKEND=l1    
    
    1
  4. Run the new file. This syntax (dot, space, and then the name of the script) runs the script in the context of the current shell, rather than in a new shell. The reason for doing this is that we want to modify the current shell's environment variables, not start a new shell, set up the environment in it, and then exit.

    . env.sh
    
    1
  5. Initialize l2geth with the genesis state. This process takes about nine minutes on my system.

    mkdir ~/gethData
    ./build/bin/geth init --datadir=$DATADIR /tmp/genesis.json --nousb
    
    1
    2
  6. Create the geth account. The private key needs to be the one specified in the configuration, otherwise the consensus algorithm fails and the node does not synchronize.

    touch $DATADIR/password
    echo $BLOCK_SIGNER_PRIVATE_KEY > $DATADIR/block-signer-key
    ./build/bin/geth account import --datadir=$DATADIR --password $DATADIR/password $DATADIR/block-signer-key
    
    1
    2
    3
  7. Start geth (logging to ~/geth.log).

    nohup build/bin/geth \
       --datadir=$DATADIR \
       --password=$DATADIR/password \
       --allow-insecure-unlock \
       --unlock=$BLOCK_SIGNER_ADDRESS \
       --mine \
       --miner.etherbase=$BLOCK_SIGNER_ADDRESS > ~/geth.log &
    
    1
    2
    3
    4
    5
    6
    7

    It is possible that geth won't listen to IPC or the TCP port (8545) until it finishes the initial synchronization.

  8. To check if l2geth is running correctly, open another command line window and run these commands:

    cd ~/optimism/l2geth
    build/bin/geth attach --datadir=~/gethData 
    eth.blockNumber
    
    1
    2
    3

    Wait a few seconds and then look at the blocknumber again and exit:

    eth.blockNumber
    exit
    
    1
    2

    If l2geth is synchronizing, the second block number is higher than the first.

  9. Wait a few hours until the entire history is downloaded by dtl and then propagated to l2geth. If you have any problems, contact us on our Discord (opens new window).