Authors: [ Ankur | Dapplooker]
System Requirements
The ZkSync node has a size of <size> GB on <date>, February, 2025.
Pre-requisite
Before starting, clean the setup then update and upgrade. Install following:
Commands
Copy 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
Copy sudo ufw enable
sudo ufw status verbose
Set explicit default UFW rules
Copy sudo ufw default deny incoming
sudo ufw default allow outgoing
Allow SSH, HTTP, and HTTPS
Copy 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
Copy sudo ufw allow from ${REMOTE.HOST.IP} to any port 3060
Setup Instructions
Clone the ZkSync Era Repository
Copy 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.
Copy 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
Copy docker compose --file mainnet-external-node-docker-compose.yml up -d
Example docker-compose file
Copy 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
Copy 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(pg_database_size('zksync_local_ext_node'));"
Ensure that your database has finished importing before proceeding to the next step.
Perform Database Migration
Copy 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
Copy 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
Copy docker ps
docker logs mainnet-node-external-node-1
Sync Status
Copy 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:
Copy {"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 :
Copy 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:
Copy {"jsonrpc":"2.0","id":83,"result":"0x2112b2d"}
REFERENCES
Last updated 16 hours ago