🐳Docker
Author [godwin]
System Requirements
8 cores+
Ubuntu 24.04
32GB+
>= 1.3TB
Last updated: 12th August 2025
Pre-Requisites
First, update, upgrade, and clean the system:
sudo apt update -y && sudo apt upgrade -y && sudo apt auto-remove -y
sudo apt install ufw -y
Configure Firewall Settings
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80
sudo ufw allow 443
sudo ufw allow 8545
sudo ufw allow 8546
sudo ufw allow 30303
sudo ufw allow 6060
Install Docker
Run this command to remove any conflicting Docker
`for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done`
Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
Add the repository to ppt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Install docker
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Test docker is working
sudo docker run hello-world
# Install docker compose
sudo apt-get update
sudo apt-get install docker-compose-plugin
# Test the docker version
docker compose version
Set up Unichain Node
Make and switch to the working directory for the ronin node
mkdir /unichain
cd unichain
Go into the docker
directory, create a docker-compose.yml
file with the following configuration:
volumes:
shared:
services:
execution-client:
image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101511.1
env_file:
- .env
- .env.mainnet
ports:
- 30303:30303/udp
- 30303:30303/tcp
- 8545:8545/tcp
- 8546:8546/tcp
volumes:
- ${HOST_DATA_DIR}:/data
- shared:/shared
- ./op-geth-entrypoint.sh:/entrypoint.sh
healthcheck:
start_interval: 5s
start_period: 240s
test: wget --no-verbose --tries=1 --spider http://localhost:8545 || exit 1
restart: always
entrypoint: /entrypoint.sh
op-node:
image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:v1.13.5
env_file:
- .env
- .env.mainnet
ports:
- 9222:9222/udp
- 9222:9222/tcp
- 9545:9545/tcp
volumes:
- ${HOST_NODE_DATA_DIR}:/data
- shared:/shared
healthcheck:
start_interval: 5s
start_period: 240s
test: wget --no-verbose --tries=1 --spider http://localhost:9545 || exit 1
depends_on:
execution-client:
condition: service_healthy
restart: always
Create a .env
file and add the following content
HOST_DATA_DIR=./geth-data
HOST_NODE_DATA_DIR=./opnode-data
Create a .env.mainnet
file in the project directory
# op-node configuration
# [required] replace with your preferred L1 (Ethereum, not Unichain) node RPC URL:
OP_NODE_L1_ETH_RPC=https://ethereum-rpc.publicnode.com
# [required] replace with your preferred L1 CL beacon endpoint:
OP_NODE_L1_BEACON=https://ethereum-beacon-api.publicnode.com
OP_NODE_NETWORK=unichain-mainnet
OP_NODE_L2_ENGINE_AUTH=/shared/jwt.hex
OP_NODE_L2_ENGINE_RPC=ws://execution-client:8551
OP_NODE_LOG_LEVEL=info
OP_NODE_LOG_FORMAT=logfmt
OP_NODE_METRICS_ADDR=0.0.0.0
OP_NODE_METRICS_ENABLED=true
OP_NODE_METRICS_PORT=3300
OP_NODE_P2P_LISTEN_IP=0.0.0.0
OP_NODE_P2P_LISTEN_TCP_PORT=9222
OP_NODE_P2P_LISTEN_UDP_PORT=9222
OP_NODE_RPC_ADDR=0.0.0.0
OP_NODE_RPC_PORT=9545
OP_NODE_VERIFIER_L1_CONFS=4
OP_NODE_P2P_NAT=true
OP_NODE_P2P_DISCOVERY_PATH=/data/opnode_discovery_db
OP_NODE_P2P_PEERSTORE_PATH=/data/opnode_peerstore_db
OP_NODE_P2P_PRIV_PATH=/data/opnode_p2p_priv.txt
OP_NODE_L2_ENGINE_KIND=geth
OP_NODE_L1_TRUST_RPC=false
# Execution Layer Sync
# op-node can drive the Execution Client to sync from the EL layer. This enables Snap Sync in op-geth or staged sync in>
# This requires the EL Client to be peered.
# By default, OP geth uses snap sync, but can use full sync (executes every block) while OP_NODE_SYNCMODE=execution-lay>
OP_NODE_SYNCMODE=execution-layer
OP_NODE_L1_RPC_KIND=standard
OP_NODE_P2P_NAT=true
OP_NODE_P2P_ADVERTISE_IP= # Replace with your real IP
OP_NODE_P2P_ADVERTISE_TCP=9222 # External port from docker-compose
OP_NODE_P2P_ADVERTISE_UDP=9222
GETH_NAT_EXTIP=<node-ext-ip>:30303
GETH_SYNCMODE=full
# op-geth configuration
GETH_OP_NETWORK=unichain-mainnet
GETH_ROLLUP_SEQUENCERHTTP=https://mainnet-sequencer.unichain.org
GETH_LOG_FORMAT=logfmt
GETH_VERBOSITY=3
GETH_DATADIR=/data
GETH_HTTP=true
GETH_HTTP_ADDR=0.0.0.0
GETH_HTTP_VHOSTS="*"
GETH_HTTP_CORSDOMAIN="*"
GETH_HTTP_API=web3,debug,eth,txpool,net,admin,rpc
GETH_WS=true
GETH_WS_ADDR=0.0.0.0
GETH_WS_API=web3,debug,eth,txpool,net,admin,rpc
GETH_AUTHRPC_JWTSECRET=/shared/jwt.hex
GETH_AUTHRPC_PORT=8551
GETH_AUTHRPC_VHOSTS="*"
GETH_AUTHRPC_ADDR=0.0.0.0
GETH_METRICS=true
GETH_METRICS_ADDR=0.0.0.0
GETH_ROLLUP_DISABLETXPOOLGOSSIP=true
GETH_TXPOOL_NOLOCALS=true
GETH_GCMODE=archive
Create op-geth-entrypoint.sh
in the project directory
#!/bin/sh
set -euxo pipefail
if [ -n "${GENESIS_FILE-}" ]; then
geth init "$GENESIS_FILE"
fi
exec geth $@
Run the node
docker-compose up -d
Monitor the node
Use docker logs to monitor the rootstock node. The -f flag ensures you are following the log output.
docker logs unichain-op-geth -f --tail 50
You should see a response similar to this once your node starts syncing
t=2025-08-12T06:46:57+0000 lvl=info msg="Imported new potential chain segment" number=24232858 hash=0xc300e3b6308be8fe5371978f01c6fab909284067fd4c43314e0a88fb65623cc1 blocks=1 txs=13 mgas=2.240231 elapsed=14.457ms mgasps=154.9476210004033 snapdiffs="1.49 MiB" triedirty="0.00 B"
t=2025-08-12T06:46:57+0000 lvl=info msg="Chain head was updated" number=24232858 hash=0xc300e3b6308be8fe5371978f01c6fab909284067fd4c43314e0a88fb65623cc1 root=0x21ef672c25a814c992dc6afda8e7bba7099b1f4af04ca534615488700f1fad6b elapsed=291.627µs
t=2025-08-12T06:46:58+0000 lvl=info msg="Imported new potential chain segment" number=24232859 hash=0xb10c669d0efc6a2faeadc3787fdf3d1fe3d9d11975a5007ea260243700d21907 blocks=1 txs=12 mgas=2.687842 elapsed=20.289ms mgasps=132.47773055462045 snapdiffs="1.50 MiB" triedirty="0.00 B"
t=2025-08-12T06:46:58+0000 lvl=info msg="Chain head was updated" number=24232859 hash=0xb10c669d0efc6a2faeadc3787fdf3d1fe3d9d11975a5007ea260243700d21907 root=0x4e69964b1c86a52c91deea8345152a6078280ba17d58d6b8872256ca3f464c70 elapsed=528.692µs
docker logs unichain-op-node -f --tail 50
t=2025-08-12T06:48:25+0000 lvl=info msg="successfully processed payload" ref=0x50665f0807456c318313e9006e79c1b6a0f711c033968e63ac7567c600cc0415:24232946 txs=13
t=2025-08-12T06:48:26+0000 lvl=info msg="Optimistically queueing unsafe L2 execution payload" id=0x2f01786fa44447e0df230dbd4219f49305a1d10abbfbb9c049f481af3486eb7d:24232947
t=2025-08-12T06:48:26+0000 lvl=info msg="Inserted new L2 unsafe block (synchronous)" hash=0x2f01786fa44447e0df230dbd4219f49305a1d10abbfbb9c049f481af3486eb7d number=24232947 newpayload_time=13.308ms fcu2_time=983.566µs total_time=14.292ms mgas=1.47295 mgasps=103.05431020568254
t=2025-08-12T06:48:26+0000 lvl=info msg="Sync progress" reason="new chain head block" l2_finalized=0x7322764ce54f000bd8ec66a188365365f36e862a6aa24a0492ec82d0dad6b81b:24231349 l2_safe=0x642c37ca5213562fe2a16cd2f808885b78ca2a67ebe6d555473eb38729c48460:24232683 l2_pending_safe=0x642c37ca5213562fe2a16cd2f808885b78ca2a67ebe6d555473eb38729c48460:24232683 l2_unsafe=0x2f01786fa44447e0df230dbd4219f49305a1d10abbfbb9c049f481af3486eb7d:24232947 l2_backup_unsafe=0x0000000000000000000000000000000000000000000000000000000000000000:0 l2_time=1754981306
t=2025-08-12T06:48:26+0000 lvl=info msg="successfully processed payload" ref=0x2f01786fa44447e0df230dbd4219f49305a1d10abbfbb9c049f481af3486eb7d:24232947 txs=10
t=2025-08-12T06:48:26+0000 lvl=info msg="Advancing bq origin" origin=0xdfa78d3e53df14ca1359db22a0f5e66320ecc465d97a7c46a018710e84bfada0:23123270 originBehind=false
Query the node
Eth syncing status
curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' http://localhost:8545 | jq
Output
If the node is done syncing, you should see the below response
{
"jsonrpc": "2.0",
"id": 1,
"result": false
}
To check the block number
curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' http://localhost:8545 | jq
Output
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x171c46f"
}
References
Last updated
Was this helpful?