🐳Docker

Authors: [ Ankur | Dapplooker]

System Requirements

CPU
OS
RAM
DISK

8 vCPU

Ubuntu 22.04

16 GB

10+ TB (SSD)

The ZkSync node has a size of 5.4 TB on February 27, 2025.

Pre-requisite

Before starting, clean the setup then update and upgrade. Install following:

  • Docker

  • Git

  • Go v1.23+

  • aria2 (optional)

Commands

sudo apt update -y && sudo apt upgrade -y && sudo apt auto-remove -y
sudo apt install docker.io git ufw -y jq -y aria2 -y

Firewall Settings

Check status & enable UFW

sudo ufw enable
sudo ufw status verbose

Set explicit default UFW rules

sudo ufw default deny incoming
sudo ufw default allow outgoing

Allow SSH, HTTP, and HTTPS

sudo ufw allow 22/tcp
sudo ufw allow 80
sudo ufw allow 443
sudo ufw allow 3322 && sudo ufw allow 3061  # Allow for P2P & Metrics

Allow Remote connection

sudo ufw allow from ${REMOTE.HOST.IP} to any port 3060

Setup Instructions

Clone the ZkSync Era Repository

git clone https://github.com/matter-labs/zksync-era.git
cd zksync-era/docs/src/guides/external-node/docker-compose-examples/

Download Snapshot

To download the Latest Snapshot visit https://en-backups.matterlabs.dev and copy the desired link. Run the below command in screen as the download may take hours/day depending upon internet speed & snapshot size.

aria2c -c -x 16 -s 16 --dir=/path/to/download --out=latest_backup.sql.gz --file-allocation=none --timeout=600 --retry-wait=30 --max-tries=0 <snapshot_link>
# Example download link
mkdir ~/zksync-era/backup/
aria2c -c -x 16 -s 16 --dir=~/zksync-era/backup/ --out=latest_backup.sql.gz --file-allocation=none --timeout=600 --retry-wait=30 --max-tries=0 http://37.27.135.10:8080/ipfs/QmVWVRjw5DEuG5noaD7Ltcc4BGGCKEcjvL8CYFw5GcAzWD/external_node_latest.pgdump.sql.gz

Start ZkSync Node

docker compose --file mainnet-external-node-docker-compose.yml up -d 

Example docker-compose file

name: "mainnet-node"
services:
  prometheus:
    image: prom/prometheus:v2.35.0
    volumes:
      - prometheus-data:/prometheus
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    expose:
      - 9090
  grafana:
    image: grafana/grafana:9.3.6
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    environment:
      GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin"
      GF_AUTH_ANONYMOUS_ENABLED: "true"
      GF_AUTH_DISABLE_LOGIN_FORM: "true"
    ports:
      - "127.0.0.1:3000:3000"
  postgres:
    image: "postgres:14"
    command: >
      postgres
      -c max_connections=200
      -c log_error_verbosity=terse
      -c shared_buffers=2GB
      -c effective_cache_size=4GB
      -c maintenance_work_mem=1GB
      -c checkpoint_completion_target=0.9
      -c random_page_cost=1.1
      -c effective_io_concurrency=200
      -c min_wal_size=4GB
      -c max_wal_size=16GB
      -c max_worker_processes=16
      -c checkpoint_timeout=1800
    expose:
      - 5430
    volumes:
      - postgres:/var/lib/postgresql/data
      - ~/zksync-era/backup/latest_backup.sql.gz:/usr/src/en/pg_backups/latest_backup.sql.gz
    healthcheck:
      interval: 1s
      timeout: 3s
      test:
        [
          "CMD-SHELL",
          'psql -U postgres -c "select exists (select * from pg_stat_activity where datname = ''{{ database_name }}'' and application_name = ''pg_restore'')" | grep -e ".f$$"',
        ]
    environment:
      - POSTGRES_PASSWORD=notsecurepassword
      - PGPORT=5430
  # Generation of consensus secrets.
  # The secrets are generated iff the secrets file doesn't already exist.
  generate-secrets:
    image: "matterlabs/external-node:2.0-v26.2.1"
    entrypoint:
      [
      "/configs/generate_secrets.sh",
      "/configs/mainnet_consensus_secrets.yaml",
      ]
    volumes:
      - ./configs:/configs
  external-node:
    image: "matterlabs/external-node:2.0-v26.2.1"
    entrypoint:
      [
      "/usr/bin/entrypoint.sh",
      "--enable-consensus",
      ]
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      generate-secrets:
        condition: service_completed_successfully
    ports:
      - "0.0.0.0:3054:3054" # consensus public port
      - "0.0.0.0:3060:3060"
      - "127.0.0.1:3061:3061"
      - "127.0.0.1:3081:3081"
      - "127.0.0.1:5000:5000"
    volumes:
      - rocksdb:/db
      - ./configs:/configs
    expose:
      - 3322
    environment:
      DATABASE_URL: "postgres://postgres:notsecurepassword@postgres:5430/zksync_local_ext_node"
      DATABASE_POOL_SIZE: 10

      EN_HTTP_PORT: 3060
      EN_WS_PORT: 3061
      EN_HEALTHCHECK_PORT: 3081
      EN_PROMETHEUS_PORT: 3322
      EN_ETH_CLIENT_URL: https://ethereum-rpc.publicnode.com
      EN_MAIN_NODE_URL: https://zksync2-mainnet.zksync.io
      EN_L1_CHAIN_ID: 1
      EN_L2_CHAIN_ID: 324
      EN_PRUNING_ENABLED: false

      EN_STATE_CACHE_PATH: "./db/ext-node/state_keeper"
      EN_MERKLE_TREE_PATH: "./db/ext-node/lightweight"
      RUST_LOG: "warn,zksync=info,zksync_core::metadata_calculator=debug,zksync_state=debug,zksync_utils=debug,zksync_web3_decl::client=error"

      EN_CONSENSUS_CONFIG_PATH: "/configs/mainnet_consensus_config.yaml"
      EN_CONSENSUS_SECRETS_PATH: "/configs/mainnet_consensus_secrets.yaml"

volumes:
  postgres: {}
  rocksdb: {}
  prometheus-data: {}
  grafana-data: {}

Import Database

docker exec -it mainnet-node-postgres-1 bash
zcat /usr/src/en/pg_backups/latest_backup.sql.gz | nohup psql -U postgres -d zksync_local_ext_node & disown

# Check Database Size
docker exec -it mainnet-node-postgres-1 psql -U postgres -c "SELECT pg_size_pretty

Ensure that your database has finished importing before proceeding to the next step.

Perform Database Migration

cd /root/zksync/repo/usr/bin

# Set migration folder as source and specify database
nohup ./sqlx migrate info --source /root/FINAL/repo/migrations --database-url postgres://postgres:password@localhost/zksync_local_ext_node & disown

# Run Database Migration
nohup ./sqlx migrate run --source /root/FINAL/repo/migrations --database-url postgres://postgres:password@localhost/zksync_local_ext_node & disown

Restart the Containers

docker compose --file ~/zksync-era/docs/src/guides/external-node/docker-compose-examples/mainnet-external-node-docker-compose.yml up -d --force-recreate

Monitoring

Monitor Logs of Docker Container

docker ps 
docker logs mainnet-node-external-node-1

Sync Status

  • Syncing status :

curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0", "method":"eth_syncing", "params":[], "id":1}' http://localhost:3060

Response should look like:

{"jsonrpc":"2.0","id":1,"result":{"startingBlock":"0x0","currentBlock":"0x3219750","highestBlock":"0x35fc3d7"}}
                                            
                                            # or
                                            
 {"jsonrpc":"2.0","id":1,"result":false} # If block is synced or < 11 block behind
  • Latest synchronized L2 block :

curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_blockNumber", "params":[],"id":1}' http://localhost:3060

Response should look like:

{"jsonrpc":"2.0","id":83,"result":"0x2112b2d"}

References

Last updated

Was this helpful?