Installation
Step-by-step installation guide for Orbinum.
Produces blocks in the permissioned testnet validator set. The RPC stays private; only P2P is public.
Nothing you type is stored by us. Values stay in this browser tab only (sessionStorage) - they survive a reload and are gone when you close the tab. A Check button sends just the values that check needs to our server, which uses them once and keeps nothing.
Before you start
- A crypto-friendly provider that allows PoS validation in writing - Hetzner and Contabo are not accepted
- Ideally a region the validator set doesn't cover yet - the application asks for provider and region
- An Orbinum account with a little test ORB from the faucet, to register your keys
Shared or burstable vCPUs aren't enough: blocks with shielded transactions carry Groth16 proofs checked during block execution, and a throttled CPU misses its slot. Ubuntu 24.04 is the only OS the official guides are tested on. State import peaks around 5-6 GB RAM - add swap under 16 GB.
nproc && free -h && df -h / && lsb_release -dsPrepare the server
1Install Docker
Orbinum ships the node as a ready-made image, so there is nothing to compile - Docker Engine and the Compose plugin are all the server needs. This adds Docker's official apt repository (Ubuntu's own docker.io package lags behind) and lets your user run docker without sudo, which every later step assumes.
Install Docker Engine + Compose
sudo apt update && sudo apt install -y ca-certificates curl git jq
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] 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 && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable --now dockerRun docker without sudo
sudo usermod -aG docker $USER
newgrp docker
docker --version && docker compose versionDocker version …, build … Docker Compose version v…
If sudo apt update ends with E: or GPG errors, stop - nothing after it will work. The "gpgv required for verification" message is misleading; find the real cause with sudo apt -o Debug::Acquire::gpgv=1 update 2>&1 | grep -iE 'denied|error' | head. Seeing cannot create /dev/null: Permission denied? Fix it with sudo rm -f /dev/null && sudo mknod -m 666 /dev/null c 1 3.
If a later docker compose pull fails with "denied", the server has an old GitHub login stored - run docker logout ghcr.io and pull again. The Orbinum image is public and needs no login.
2Open the P2P port
Other validators and nodes connect to you on the P2P port - it is the only port a validator needs open. SSH is allowed first so turning the firewall on can't lock you out.
Other nodes dial you here, so it must be reachable. Change it only if 30333 is taken.
sudo ufw allow 22/tcp
sudo ufw allow 30333/tcp comment 'orbinum p2p'
sudo ufw --force enable
sudo ufw statusStatus: active 22/tcp ALLOW Anywhere 30333/tcp ALLOW Anywhere # orbinum p2p
Docker publishes container ports through its own iptables rules, which bypass ufw - so ufw is NOT what keeps the validator's RPC private; RPC_BIND=127.0.0.1 in .env is. That RPC runs with unsafe methods: anyone who reaches it can read or replace your session keys. Never set RPC_BIND to 0.0.0.0 or a public IP.
SSH on a custom port? Allow that port instead of 22.
Run the node
3Download node-deploy
The official deploy repo holds a Docker Compose stack per role and the testnet chain spec (genesis and boot nodes). You work from its testnet/validator folder from here on.
git clone https://github.com/orbinum/node-deploy.git $HOME/node-deploy
cd $HOME/node-deploy/testnet/validator
[ -f .env ] || cp .env.example .envAlready cloned it for another role? Skip the clone and run git -C $HOME/node-deploy pull instead. An .env you already filled is never overwritten.
4Configure your node
All settings live in .env. Fill in the fields and the command writes them for you, generates the node key (your node's P2P identity - only on the first run, so it is safe to run again) and prints the result to check.
Shown on telemetry and in your application - pick something people recognise.
Only where Docker publishes the RPC on this server (127.0.0.1). The node itself listens on 9944 inside the container, and the commands in this guide go through docker exec, so they always use 9944. Change this only if another service on the server already uses port 9944.
Fetches the latest finalized state instead of replaying every block - about 5 minutes to the chain head on a test run (September 2026, ~560 MiB of state). Blocks before the sync point are kept as headers only - fine for producing blocks, not for serving history. Only applies to an empty data volume.
cd $HOME/node-deploy/testnet/validator
sed -i "s|^VALIDATOR_NAME=.*|VALIDATOR_NAME=<your-validator-name>|" .env
grep -q '^VALIDATOR_NODE_KEY=.' .env || sed -i "s|^VALIDATOR_NODE_KEY=.*|VALIDATOR_NODE_KEY=$(openssl rand -hex 32)|" .env
sed -i "s|^SYNC_MODE=.*|SYNC_MODE=--sync warp|" .env
sed -i "s|^P2P_PORT=.*|P2P_PORT=30333|" .env
sed -i "s|^RPC_PORT=.*|RPC_PORT=9944|" .env
chmod 600 .env
grep -E '^(VALIDATOR_NAME|SYNC_MODE|P2P_PORT|RPC_PORT|RPC_BIND)=' .envVALIDATOR_NAME=<your-validator-name> SYNC_MODE=--sync warp P2P_PORT=30333 RPC_PORT=9944 RPC_BIND=127.0.0.1
Back up .env. The node key is your node's peer ID - keep it when you move servers, and never run two nodes with the same key.
5Start the node
Pulls the image and starts two containers: the validator, and Watchtower, which checks for a new testnet image every 5 minutes and restarts the node on it - node updates are automatic.
Start
cd $HOME/node-deploy/testnet/validator
docker compose pull
docker compose up -dFollow the logs (Ctrl+C stops following, not the node)
cd $HOME/node-deploy/testnet/validator && docker compose logs -f --tail 50 orbinum-validatorWarp sync is complete, continuing with state sync.
⚙️ State sync, Downloading state, 69%, 388.11 Mib (8 peers), best: #0 …
<- the % can sit still while the Mib keeps rising - that is progress
⚙️ State sync, Importing state, 69%, 560.62 Mib (8 peers), best: #0 …
<- then nothing moves for a few minutes: the state is being written to disk
State sync is complete, continuing with block sync.
🏆 Imported #997485 (0x8e26…355d → 0xbcd2…59d0) <- at the chain head
⏩ Block history, #58176 (8 peers), best: #997486 …
<- old block headers backfill in the background; the node already worksAt startup the node benchmarks the server and may log The hardware does not meet the minimal requirements ... for role 'Authority'. It is a warning, not an error - the node keeps running. Look at which check failed: the targets are about 1000 MiB/s for BLAKE2-256 (CPU), 950 MiB/s sequential and 420 MiB/s random disk writes. A disk well below that risks missed slots once you are in the validator set. A single Block import error: Incomplete block import pipeline around the switch from warp to state sync is harmless as long as the log keeps moving.
Changed .env later? Apply it with docker compose up -d --force-recreate - a plain restart keeps the old settings, because a container's command is fixed when it is created.
6Wait for the sync
Session keys can only be generated on a synced node. The first command shows whether it is still syncing, the second how far it still has to go. Your node should also appear on telemetry under <your-validator-name> within a minute.
Health
docker exec orbinum-validator curl -sS -H 'Content-Type: application/json' -d '{"id":1,"jsonrpc":"2.0","method":"system_health","params":[]}' http://localhost:9944 | jqProgress
docker exec orbinum-validator curl -sS -H 'Content-Type: application/json' -d '{"id":1,"jsonrpc":"2.0","method":"system_syncState","params":[]}' http://localhost:9944 | jq"isSyncing": false, "peers": 8, "shouldHavePeers": true
peers stays at 0? The server can't reach the network - check that outbound traffic is allowed and the P2P port is open.
Become a validator
7Create and fund your account
A validator is registered from a normal Orbinum account. Create one in Talisman, SubWallet or the Polkadot.js extension - the address starts with 5 - and enter it below: its public key is read straight from the address. Claim test ORB from the faucet now too; you need it in step 9, where registering the keys is a signed transaction that pays a fee.
Checked in your browser, including the address checksum - a mistyped character is caught. It is not looked up on-chain, so make sure it is the account you control.
Worked out from your address in the browser - the next step uses it. No copy-paste needed.
Optional: cross-check on the node
docker exec orbinum-validator orbinum-node key inspect <your-5…-address>Public Key URI `<your-5…-address>` is account: Network ID/Version: substrate Public key (hex): <account-public-key-hex> <- same as above Account ID: <account-public-key-hex> SS58 Address: <your-5…-address>
8Generate session keys
Asks the node to create its Aura (block production) and GRANDPA (finality) keys inside its own keystore, and to sign a proof that they belong to your account - the public key from the previous step is already filled into the command. The private keys never leave the server; only the public half goes on-chain. Run it once, then paste keys and proof from the output below.
docker exec orbinum-validator curl -sS -H 'Content-Type: application/json' -d '{"id":1,"jsonrpc":"2.0","method":"author_rotateKeysWithOwner","params":["<account-public-key-hex>"]}' http://localhost:9944 | jq .result{
"keys": "0x…", <- 128 hex characters
"proof": "0x…"
}Only the format is checked here. Copy it exactly from this server's output.
Only the format is checked here. It must come from the same run as keys.
Run this on the validator server, and only once. Every run generates a new pair and the keys and proof you saved become stale - rotate after registering and you must submit session.setKeys again with the new pair, or the node stops authoring at the next session.
No proof in the result? The node is still on an older runtime - wait until isSyncing is false and run it again.
9Register the keys on-chain
Submit session.setKeys from the same account whose public key you used. First run the check below on the server - it confirms the node holds the keys you pasted, so a wrong paste is caught before you pay for a transaction. Then in Polkadot.js Apps open Developer → Extrinsics, select that account, choose session → setKeys(keys, proof), copy the values below in and sign.
Appears once keys is filled inAppears once proof is filled inAppears once keys is filled inAppears once keys is filled inBefore signing: the node holds these keys
docker exec orbinum-validator curl -sS -H 'Content-Type: application/json' -d '{"id":1,"jsonrpc":"2.0","method":"author_hasSessionKeys","params":["<keys>"]}' http://localhost:9944 | jq .resulttrue
Session.InvalidProof means the proof doesn't match the signing account - sign with the exact account whose public key you passed in the previous step.
false? The keys you pasted aren't in this node's keystore - paste them again from the previous step's output on this server. The Cheatsheet's "Check Aura and GRANDPA Separately" shows which half is missing.
10Verify
Two things must be true before you apply, both checked from our server with the buttons below: the chain has your keys registered for your account (the same keys the node holds, checked in the previous step - if the chain points to keys the node doesn't hold, you miss your blocks), and other nodes can reach the node's P2P port.
Run curl -4 ifconfig.me on the server if you don't know it.
The P2P check connects from our server and waits for the libp2p handshake, so a pass means the node itself answered - not just an open port. Prefer your own machine? nc -vz <your-server-ip> 30333 from anywhere outside the server.
11Apply to the validator set
The testnet validator set is permissioned - up to 32 validators, no bonding. It is a human review, not a scored form: no SLA and no guaranteed slot, and a short honest email reads better than a long one. The team confirms your node is synced on telemetry before approving. Once added, the node starts producing blocks after two session rotations (about 2 hours).
Who is being added - a company page, a GitHub org or a personal handle. Also the email subject.
Networks you already validate or run. "First validator" is a fine answer.
Both are selection factors - the team spreads validators across providers and regions.
So the team can reach you when your node misbehaves.
Optional - how you would notice an outage before they do.
To: contact@orbinum.net
Subject: Testnet validator application - <operator name / website>
- SS58 address: <your-5…-address>
- Operator: <operator name / website>
- Experience: <networks you already validate>
- Provider / region: <provider, region>
- Contact: <Discord or Telegram handle>
- Telemetry node name: <your-validator-name>
- Monitoring: -Once you're in, telemetry shows a ✓ next to your node in the authority set - that's what the validator's telemetry level 1 is for.