This guide walks you through setting up a Base Mainnet Full Node using geth, downloading a pre-synced snapshot, and monitoring synchronization progress. The node is managed using Docker and docker-compose for reliability and portability.
- CPU: 8 cores minimum (e.g., AMD EPYC or Intel Xeon)
- RAM: 32 GB minimum
- Disk: 10 TB NVMe SSD (essential for full sync speed and IOPS)
- Network: At least 100 Mbps (preferably 1 Gbps)
- OS: Ubuntu 22.04 LTS recommended
sudo apt update -y && sudo apt upgrade -y
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove -y $pkg; done
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] 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 update -y && sudo apt upgrade -y
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Test Docker
sudo docker run hello-world
sudo systemctl enable docker
sudo systemctl restart dockergit clone https://github.com/base/node.git base-node
cd base-nodenano .env.mainnetPaste and update the following with your preferred values:
OP_NODE_L1_ETH_RPC=<your-preferred-l1-rpc>
OP_NODE_L1_BEACON=<your-preferred-l1-beacon>
OP_NODE_L1_BEACON_ARCHIVER=<your-preferred-l1-beacon-archiver>You can download a recent snapshot from either the official Base snapshot site or PublicNode, which offers smaller and faster alternatives.
mkdir -p /home/base-node/{snapshots,geth-data}
aria2c -x 16 -s 16 -k 2M -d /home/base-node/snapshots -o base-geth-full.zst \
"https://mainnet-full-snapshots.base.org/$(curl -s https://mainnet-full-snapshots.base.org/latest)" && \
tar -I zstd -xvf /home/base-node/snapshots/base-geth-full.zst -C /home/base-node/geth-dataAfter extraction, your Geth data will be under:
/home/base-node/geth-data/snapshots/mainnet/download/geth
You can also use https://www.publicnode.com/snapshots to find smaller Geth snapshots for Base. Choose the latest .tar.lz4 file listed under Base.
To download and extract:
mkdir -p /home/base-node/geth-data
aria2c -x 16 -s 16 -k 2M -o - "https://snapshots.publicnode.com/base-geth-part-33474880.tar.lz4" | \
lz4 -d | tar -xvf - -C /home/base-node/geth-datamv /home/base-node/geth-data/snapshots/mainnet/download/geth /home/base-node/geth-dataEnsure structure is:
/home/base-node/geth-data/geth/chaindata
docker compose up --build -dTo monitor your running containers and check sync status, follow these steps:
- List all active containers:
docker ps
This will show you container names (e.g., base-node-execution-1) that you can use in the next commands.
- Check logs for your execution container:
docker logs -f base-node-execution-1- Quick block comparison (local vs remote):
echo "Local Block:" $(docker exec base-node-execution-1 geth attach --exec eth.blockNumber /data/geth.ipc) && echo "Remote Block:" $(curl -s https://api.basescan.org/api?module=proxy&action=eth_blockNumber | jq -r '.result' | xargs printf "%d
")
```bash
docker logs -f base-node-execution-1Save this as check_sync.sh:
#!/bin/bash
REMOTE_BLOCK=$(curl -s https://api.basescan.org/api?module=proxy&action=eth_blockNumber&apikey=YourApiKeyToken | jq -r '.result')
REMOTE_BLOCK_DEC=$((16#${REMOTE_BLOCK:2}))
START_BLOCK=$(docker exec base-node-execution-1 geth attach --exec "eth.blockNumber" /data/geth.ipc)
SLEEP_TIME=5
echo "β³ Checking sync progress..."
while true; do
LOCAL_BLOCK=$(docker exec base-node-execution-1 geth attach --exec "eth.blockNumber" /data/geth.ipc)
PROGRESS=$(awk "BEGIN { printf \"%.2f\", ($LOCAL_BLOCK / $REMOTE_BLOCK_DEC) * 100 }")
REMAINING=$((REMOTE_BLOCK_DEC - LOCAL_BLOCK))
if [[ $REMAINING -le 0 ]]; then
echo "β
Sync Complete!"
break
fi
if [[ -z "$BLOCKS_PER_SEC" ]]; then
BLOCKS_PER_SEC=$(( ($LOCAL_BLOCK - $START_BLOCK) / 1 ))
START_TIME=$(date +%s)
else
CURRENT_TIME=$(date +%s)
ELAPSED=$((CURRENT_TIME - START_TIME))
BLOCKS_PER_SEC=$((($LOCAL_BLOCK - $START_BLOCK) / ($ELAPSED > 0 ? $ELAPSED : 1)))
fi
ETA_SECONDS=$((REMAINING / (BLOCKS_PER_SEC > 0 ? BLOCKS_PER_SEC : 1)))
ETA_H=$((ETA_SECONDS / 3600))
ETA_M=$(((ETA_SECONDS % 3600) / 60))
ETA_S=$((ETA_SECONDS % 60))
echo "π¦ Local Block: $LOCAL_BLOCK"
echo "π Remote Block: $REMOTE_BLOCK_DEC"
echo "π Progress: $PROGRESS% | Remaining: $REMAINING blocks | ETA: ${ETA_H}h ${ETA_M}m ${ETA_S}s"
echo "--------------------------------------------"
sleep $SLEEP_TIME
REMOTE_BLOCK=$(curl -s https://api.basescan.org/api?module=proxy&action=eth_blockNumber&apikey=YourApiKeyToken | jq -r '.result')
REMOTE_BLOCK_DEC=$((16#${REMOTE_BLOCK:2}))
doneMake it executable:
chmod +x check_sync.sh
./check_sync.shOnce your Local Block is equal to or just a few blocks behind the Remote Block (checked via basescan.org), your node is fully synced and production-ready.
Your Base full node exposes the RPC interface (port 8545) by default via Docker.
To access your node's RPC endpoint, simply use:
http://<your-server-ip>:8545
To test it from your local machine, you can run:
curl -X POST http://<your-server-ip>:8545 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'You should get a response with the latest block number like:
{"jsonrpc":"2.0","id":1,"result":"0x123456"}Replace <your-server-ip> with the actual IP address of your node server.
- Join Base Discord for community support
- Recheck your volume mount paths and snapshot extraction paths
- Ensure Docker has access to enough CPU/IO/space
Enjoy your Base Full Node π