Skip to main content

Gno.land Test13 Docker Validator Setup

This guide deploys a Gno test13 node with Docker Compose. gnoland runs in a container and stores chain data under /data/gno/gnoland-data on the host. gnokey runs on the host, so the source build still installs both gnoland and gnokey.

tip

The commands below assume a root shell. If you use a non-root user, prefix system package and firewall commands with sudo.

Variables

Set all operator-specific values once before running the guide:

export GNO_HOME=/data/gno
export GNO_REPO=${GNO_HOME}/gno
export GNO_DATA=${GNO_HOME}/gnoland-data
export GNO_CHAIN_ID=test-13
export GNO_IMAGE=gnoland:test13
export GNO_BRANCH=chain/test13
export GNO_GENESIS_URL=https://github.com/gnolang/gno/releases/download/chain/test13/genesis.json
export GNO_RPC=https://rpc.test13.testnets.gno.land:443
export GNO_P2P_PORT=26656
export GNO_RPC_PORT=26657
export GNO_KEY_NAME=operator
export GNO_PUBLIC_IP=$(curl -s https://ipinfo.io/ip)
export GNO_SEEDS=g1rrj6gvxp7ph8d4rsy9vegrhp8nx4lyxzgmc4ad@gnoland-testnet-seed.itrocket.net:54656
export GNO_PERSISTENT_PEERS=g1e3pftctakyx58mzapqs4syc4h0jcxwtnraq00c@185.248.24.16:47656,[email protected]:26656,[email protected]:32556,[email protected]:41656,[email protected]:26656,[email protected]:17656,[email protected]:26656,[email protected]:17656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:54656,[email protected]:22656,[email protected]:27656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:26656,[email protected]:30800,[email protected]:37656,[email protected]:26656

The example key name operator is only a local keyring label. Choose a label that helps you recognize the wallet on the server.

System Requirements

ComponentRecommended
CPU4+ cores
Memory8GB+ RAM
Storage200GB+ NVMe SSD
OSUbuntu 22.04/24.04 LTS
NetworkPublic TCP 26656 for P2P; restrict RPC 26657 unless you intentionally expose it

Install Dependencies

apt update
apt install -y git make build-essential curl wget jq lz4 ufw ca-certificates ripgrep

Install Go if it is not already available:

go version

Gno test13 builds with a recent Go toolchain. If go version is missing or too old, install Go from the official Go downloads page before continuing.

Install Docker Engine with the Compose plugin:

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
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" \
> /etc/apt/sources.list.d/docker.list

apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

systemctl enable --now docker
docker --version
docker compose version

Build Gno and Host Binaries

mkdir -pv ${GNO_HOME}
cd ${GNO_HOME}

git clone https://github.com/gnolang/gno.git
cd ${GNO_REPO}
git checkout ${GNO_BRANCH}

make -C gno.land install.gnoland install.gnokey
docker build --target gnoland -t ${GNO_IMAGE} .

docker run --rm ${GNO_IMAGE} version
gnokey version

gnoland is built into the Docker image and node operations below use Docker or Docker Compose. gnokey is used directly on the host for keys and transactions.

Create Data Directories and Genesis

mkdir -pv ${GNO_DATA}/config ${GNO_DATA}/secrets
cd ${GNO_HOME}

wget -O ${GNO_DATA}/genesis.json ${GNO_GENESIS_URL}
shasum -a 256 ${GNO_DATA}/genesis.json

Compare the hash with the checksum published by the official test13 release notes before starting the node.

Create Docker Compose File

cat > ${GNO_HOME}/docker-compose.yaml << EOF
services:
gnoland:
image: ${GNO_IMAGE}
container_name: gnoland-test13
restart: unless-stopped
ports:
- "${GNO_P2P_PORT}:26656"
- "127.0.0.1:${GNO_RPC_PORT}:26657"
volumes:
- ${GNO_DATA}:/gnoland-data
command: >
start
-chainid ${GNO_CHAIN_ID}
-data-dir /gnoland-data
-genesis /gnoland-data/genesis.json
-skip-genesis-sig-verification=true
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
EOF

RPC is bound to 127.0.0.1 in this example. Put a reverse proxy and access control in front of it if remote RPC access is required.

Initialize Config and Secrets

Run gnoland configuration commands through Docker Compose:

cd ${GNO_HOME}

docker compose run --rm gnoland config init -config-path /gnoland-data/config/config.toml
docker compose run --rm gnoland secrets init -data-dir /gnoland-data/secrets

Update the generated config with test13 networking values:

python3 - <<'PY'
import os
from pathlib import Path

path = Path(os.environ["GNO_DATA"]) / "config" / "config.toml"
text = path.read_text()
public_ip = os.environ["GNO_PUBLIC_IP"]
seeds = os.environ["GNO_SEEDS"]
persistent_peers = os.environ["GNO_PERSISTENT_PEERS"]
replacements = {
'db_backend = "lmdbdb"': 'db_backend = "pebbledb"',
'moniker = ""': 'moniker = "gno-test13-node"',
'prune_strategy = "everything"': 'prune_strategy = "syncable"',
'timeout_commit = "1s"': 'timeout_commit = "3s"',
'peer_gossip_sleep_duration = "100ms"': 'peer_gossip_sleep_duration = "10ms"',
'flush_throttle_timeout = "100ms"': 'flush_throttle_timeout = "10ms"',
'laddr = "tcp://127.0.0.1:26657"': 'laddr = "tcp://0.0.0.0:26657"',
'laddr = "tcp://127.0.0.1:26656"': 'laddr = "tcp://0.0.0.0:26656"',
'external_address = ""': f'external_address = "{public_ip}:26656"',
'seeds = ""': f'seeds = "{seeds}"',
'persistent_peers = ""': f'persistent_peers = "{persistent_peers}"',
}
for old, new in replacements.items():
text = text.replace(old, new)
path.write_text(text)
PY

Review peer settings before startup:

rg -n "moniker|external_address|seeds|persistent_peers|db_backend|laddr" ${GNO_DATA}/config/config.toml

Open P2P Port

ufw allow ${GNO_P2P_PORT}/tcp
ufw status

If your cloud provider has a separate firewall, allow TCP 26656 there as well.

Optional: Restore Snapshot

If you use a trusted snapshot, stop the node first and extract the snapshot into the mounted data directory:

cd ${GNO_HOME}
docker compose down

curl -L http://146.19.24.32:8000/gno_test13/gno-test13-snapshot.tar.lz4 \
| lz4 -c -d - \
| tar -x -C ${GNO_DATA} \
--exclude='./config' \
--exclude='./config/*' \
--exclude='./secrets' \
--exclude='./secrets/*' \
--exclude='./genesis.json'

Only restore snapshots from sources you trust. Keep config/, secrets/, and genesis.json intact.

Start the Node

cd ${GNO_HOME}
docker compose up -d
docker compose ps
docker compose logs -f

Check node status from the host:

curl -s http://127.0.0.1:${GNO_RPC_PORT}/status | jq

Wait until catching_up is false before sending validator registration transactions.

Create a Host Wallet

Run gnokey on the host:

gnokey add ${GNO_KEY_NAME}
gnokey list

Back up the mnemonic immediately. Fund the resulting Gno address with enough ugnot for registration and later operations.

Confirm the account is visible on test13:

gnokey query -remote "${GNO_RPC}" auth/accounts/$(gnokey list | awk '/address:/ {print $2; exit}')

Read Validator Key from the Container Data

The validator private key lives under the mounted /gnoland-data/secrets directory and is read through Docker Compose:

docker compose run --rm gnoland secrets get validator_key -data-dir /gnoland-data/secrets

Use the returned validator address and public key in the registration command below. Keep the key files under ${GNO_DATA}/secrets backed up and private.

Register the Validator

The registration transaction is a gnokey transaction from the host. The example below avoids separate placeholder variables for the public profile fields; replace the human-readable example strings inline.

gnokey maketx call \
--pkgpath gno.land/r/gnops/valopers \
--func Register \
--args "gno-test13-node" \
--args "Gno test13 validator running Docker Compose with host-side gnokey operations and Prometheus monitoring." \
--args "data-center" \
--args "g1examplevalidatoraddressxxxxxxxxxxxxxxxxxx" \
--args "gpub1examplevalidatorpublickeyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
--gas-fee 1000000ugnot \
--gas-wanted 50000000 \
--chainid ${GNO_CHAIN_ID} \
--remote ${GNO_RPC} \
--broadcast \
${GNO_KEY_NAME}

The first two --args are examples for the public profile shown in the valopers registry. The validator address and public key come from docker compose run --rm gnoland secrets get validator_key -data-dir /gnoland-data/secrets.

Maintenance Commands

cd ${GNO_HOME}

docker compose ps
docker compose logs --tail=100 gnoland
docker compose logs -f gnoland
docker compose restart gnoland
docker compose down
docker compose up -d

Check disk usage:

du -sh ${GNO_HOME} ${GNO_DATA} ${GNO_DATA}/*
df -h

Backup

Back up the key material and config before upgrades:

tar -czf ${GNO_HOME}/gno-test13-config-secrets-$(date +%Y%m%d).tar.gz \
-C ${GNO_DATA} \
config secrets genesis.json

Store the archive outside the validator host. Never publish secrets/ or wallet mnemonics.

References