Validator Operations
Day-two work: knowing your node is healthy, keeping the one secret you cannot regenerate, and recognising the failures that matter.
Telemetry
Your node reports to telemetry.orbinum.network by default — block height, finalized height, peer count, transaction pool, version and approximate location.
The Compose file already sets this. Three parts of the value are load-bearing:
TELEMETRY_URL=--telemetry-url "wss://telemetry.orbinum.io/submit/ 1"
- The quotes stay. The node parses
"<url> <level>"as a single argument. Without them the level is read as a separate flag and startup fails. - The trailing slash stays.
/submitwithout it does not upgrade. 1is the verbosity level, and for a validator it is not optional: level 1 sendsafg.authority_set, the only message carrying your validator address. That is what populates the Address column. RPC nodes stay at0— they have no address to report.
To opt out entirely, set the variable to empty and recreate the container. Telemetry is an outbound connection, so it opens no port and works on a node whose RPC is loopback-only.
A container's command line is fixed at creation. docker compose restart brings
the node back with its old arguments:
docker compose up -d --force-recreate orbinum-validator
Metrics and alerts
Prometheus metrics are exposed on 9615, bound by METRICS_BIND — keep it on a
private interface.
The four alerts worth having, in Prometheus rule form:
groups:
- name: orbinum-validator
interval: 30s
rules:
- alert: NodeOffline
expr: up{job="orbinum-node"} == 0
for: 2m
labels: { severity: critical }
- alert: LowPeerCount
expr: substrate_sub_libp2p_peers_count < 3
for: 5m
labels: { severity: warning }
- alert: FinalityStalled
expr: |
(substrate_block_height{status="best"} -
substrate_block_height{status="finalized"}) > 100
for: 5m
labels: { severity: critical }
- alert: HighMemoryUsage
expr: |
(process_resident_memory_bytes /
node_memory_MemTotal_bytes) > 0.9
for: 10m
labels: { severity: warning }
FinalityStalled is the one to act on first. GRANDPA needs more than two thirds
of the set voting; with a small validator set, a couple of nodes offline stops
finality for everyone while block production continues. A growing gap between best
and finalized is the signal.
Back up your keystore
Chain data lives in a named Docker volume and can always be resynced. The keystore cannot. It holds the private halves of your session keys.
# Copy the keystore out of the container, then encrypt it
docker cp orbinum-validator:/data/chains/orbinum_testnet/keystore ./keystore-backup
tar -czf keystore-$(date +%Y%m%d).tar.gz keystore-backup
gpg --symmetric --cipher-algo AES256 keystore-*.tar.gz
rm -rf keystore-backup
Restoring this backup onto a second machine while the first is still running makes both sign with the same identity. On Orbinum that costs you nothing today — there is no slashing — but it produces conflicting votes that damage finality for the whole network, and it is exactly what slashing will punish once it exists.
If you are migrating, stop the old node first and confirm it is stopped.
Losing the keystore is recoverable: generate new session keys with
author_rotateKeys and submit session.setKeys again. You keep your slot; you
just miss blocks until the next session.
Updates
Watchtower ships in the Compose stack and polls every 5 minutes, rolling-restarting the node when a new image is published. Nothing to do.
To force an update immediately:
cd node-deploy/testnet/validator
docker compose pull && docker compose up -d
Check what Watchtower has done:
docker compose logs orbinum-watchtower
Runtime upgrades apply through on-chain governance and need no operator action — the node adopts the new runtime when the upgrade block finalizes.
Troubleshooting
The node has no peers
Almost always one of two things:
- Port
30333is not reachable from the internet. Open inufwis not enough — check your provider's security group, and any NAT in front of the host. Test from elsewhere:nc -z <your-ip> 30333. RESERVED_NODESis set. A non-empty value activates--reserved-only, which makes the node refuse every peer not on the list — the bootnodes included. Public validators leave it empty. See step 2.
The node is synced but never authors
Work through these in order:
| Check | How | Expected |
|---|---|---|
| Session keys are on-chain | session.nextKeys(yourAccount) in Chain state | Returns your key blob |
| You are in the approved set | validatorSet.approvedValidators | Contains your account |
| You are in the active set | session.validators | Contains your account |
| Enough time has passed | — | 2 session boundaries ≈ 2 h since addValidator |
If approvedValidators contains you but session.validators does not, and more
than two hours have passed, the usual cause is that the keystore no longer holds
the keys registered on-chain — you rotated keys after setKeys, or restored the
container without the volume. Re-run author_rotateKeys and submit setKeys
again.
Finality is stalled
Check the best-versus-finalized gap on telemetry. If block production continues but finality does not advance, the network is short of GRANDPA votes — not something one operator fixes alone. Keep your node up and reachable, and tell the team.
The container will not start
docker compose logs --tail 100 orbinum-validator
Two common causes: port 30333 already in use on the host, or the chain-spec
mount path is wrong after moving the checkout. Both name themselves clearly in the
logs.
docker compose down -v deletes the volume, forcing a full resync — hours of it.
It also deletes the keystore unless you have a backup. Exhaust everything else
first.
Next Steps
- Relay Setup and Rewards — claim the fees your blocks earn
- Consensus Mechanism — why finality needs a quorum
- RPC and Archive Nodes — running a public endpoint instead