Skip to main content

Public RPC Node

The internet-facing role: serves wallets, dApps and explorers over HTTPS/WSS, and acts as a bootnode for the network. It runs no consensus and holds no keys.

Prerequisites

Docker, a GHCR token, and a domain. See Installation — and Running a Node if you are still choosing a role.

node-deploy is canonical for the deployment steps

testnet/rpc/ in node-deploy ships the compose file, the Caddy config and the chain spec, and its docs/rpc-node.md is the step-by-step walkthrough. This page explains why each piece is configured the way it is.


Configuration Reference

The testnet flags are set for you in the compose files. The reference below documents what each node type actually runs.

RPC / bootnode (testnet/rpc/)

--chain /chain-specs/testnet-spec.json
--name "${RPC_NAME}"
--base-path /data
--listen-addr /ip4/0.0.0.0/tcp/30333
--node-key ${RPC_NODE_KEY} # must derive the PeerId baked into bootNodes
--rpc-port 9944
--rpc-cors all
--rpc-external
--rpc-methods Safe # read-only — recommended for public endpoints
--rpc-max-connections 1000
--state-pruning archive # full historical state (explorers/indexers)
--blocks-pruning archive
--no-mdns
--no-private-ipv4
--prometheus-external
--prometheus-port 9615

RPC nodes use host networking and sit behind Caddy + Cloudflare (see RPC Endpoint Protection).

Sizing disk for the shielded pool

The active tree stores internal Merkle nodes on-chain, not just leaves, so that proofs are served in O(depth) rather than recomputed from scratch. Budget for state that grows with every commitment, not only with block count — and more steeply on archive nodes.

Once a tree fills and seals, an on_idle sweep prunes ~99.8% of its internal MerkleNodes; a proof for a note in a sealed tree recomputes the pruned siblings from the leaves on demand. The sealed root stays a permanent anchor that keeps older notes spendable — only the redundant internal nodes are reclaimed, so per-tree storage drops sharply after sealing.

RPC method safety

Public RPC nodes run --rpc-methods Safe. Never expose --rpc-methods Unsafe (which enables author_*) through Cloudflare. If a backend needs unsafe methods (e.g. a faucet), run a separate node reachable only over the private network / loopback.


Monitoring and Observability

Prometheus Metrics

Orbinum exposes Substrate and custom metrics on the --prometheus-port (default: 9615).

Key metrics to monitor:

MetricDescriptionAlert Threshold
substrate_block_height{status="best"}Latest block received-
substrate_block_height{status="finalized"}Latest finalized blockLag > 100 blocks
substrate_sub_libp2p_peers_countConnected peers< 3 peers
substrate_sub_txpool_validations_finishedProcessed transactions-
process_cpu_seconds_totalCPU usage> 80% sustained
process_resident_memory_bytesRAM usage> 90% of available

Prometheus scrape configuration:

scrape_configs:
- job_name: 'orbinum-node'
scrape_interval: 15s
static_configs:
- targets: ['localhost:9615']
labels:
instance: 'testnet-rpc-1'

Health Check Endpoints

System health:

curl -H "Content-Type: application/json" \
-d '{"id":1, "jsonrpc":"2.0", "method": "system_health"}' \
http://localhost:9944

Expected response:

{
"jsonrpc": "2.0",
"result": {
"isSyncing": false,
"peers": 23,
"shouldHavePeers": true
},
"id": 1
}

Sync status:

curl -H "Content-Type: application/json" \
-d '{"id":1, "jsonrpc":"2.0", "method": "system_syncState"}' \
http://localhost:9944
Alert rules live with the validator docs

The four rules worth having — node offline, low peers, finality stalled, high memory — are written out in Validator Operations. They apply unchanged here; only the scrape target differs.


Maintenance Operations

Database Management

Pruning

The public RPC / bootnode runs in archive mode (--state-pruning archive --blocks-pruning archive) so explorers and indexers can query full history. Validators use the chain spec defaults.

Check database size (inside the container):

docker exec orbinum-rpc-node du -sh /data/chains/orbinum_testnet/db/

Backups

Chain data lives in a named Docker volume (rpc-node-data) and can always be resynced from the network, so an RPC node needs no backup of its own — it holds no key material that matters.

Validators do have something to back up

A validator's keystore is the one thing it cannot regenerate. See Validator Operations.

Software Updates

Watchtower ships in the compose stack and rolling-restarts the node when a new image is published — same mechanism as every other role, documented in Validator Operations. The only difference is the directory:

cd node-deploy/testnet/rpc
docker compose pull && docker compose up -d

Troubleshooting

Node Won't Start

Symptoms: Container exits immediately after docker compose up

Diagnosis:

docker compose logs --tail 50 orbinum-rpc-node

# Common errors:
# - "Address already in use" → P2P port 30333 conflict on the host
# - chain-spec mount errors → testnet-spec.json not mounted / wrong path

Solutions:

# Free port 30333 (find the conflicting process)
sudo ss -ltnp | grep 30333

# Recreate after editing .env / compose
docker compose up -d --force-recreate

Sync Stalled or No Peers

Symptoms: Block height not increasing, peers: 0 for an extended period

Diagnosis (RPC node — RPC is reachable on loopback):

curl -s http://127.0.0.1:9944 -H "Content-Type: application/json" \
-d '{"id":1,"jsonrpc":"2.0","method":"system_health"}' | jq '.result.peers'

Solutions:

# Confirm P2P port 30333 is reachable
nc -zv <PUBLIC_OR_PRIVATE_IP> 30333

# Force database rebuild (LAST RESORT — wipes the chain data volume)
docker compose down -v
docker compose up -d

High Memory Usage

Symptoms: Node OOM-killed under query load

The RPC node is capped via mem_limit / cpus in testnet/rpc/docker-compose.yml (defaults RPC_MEM_LIMIT=6g, RPC_CPUS=3) so a query flood can't starve Caddy and the OS.

Diagnosis:

docker stats orbinum-rpc-node --no-stream

Solutions:

# Tune the caps in .env, then recreate. Leave ~2 GB RAM + 1 core for OS + Caddy.
RPC_MEM_LIMIT=12g
RPC_CPUS=6

Security Hardening

A public RPC endpoint is an attack surface in a way a validator is not: it accepts requests from anyone, by design. The layers below are what keep that from becoming a liability.

Validator hardening is different

A validator publishes no RPC at all. Its security model — closed ports, keystore handling, key hygiene — is covered in Validator Operations.

RPC Endpoint Protection

The public RPC endpoint is served behind Cloudflare, with Caddy on the origin terminating TLS using a Cloudflare Origin Certificate (origin.pem / origin.key) — not Let's Encrypt/certbot, not nginx. The custom Caddy image bundles the caddy-ratelimit plugin. Full walkthrough: docs/rpc-node.md in node-deploy.

Defense-in-depth layers:

LayerWhat it does
Cloudflare proxy (orange-cloud DNS)L3/L4 + L7 DDoS protection in front of the origin
Cloudflare rate limit100 requests / 10s per IP (primary)
Cloudflare WAF skip ruleSkips managed bot challenges on the RPC host — otherwise non-browser clients (indexers, wallets, server-side dApps) get JS-challenged and fail
Caddy rate limit100 / 10s keyed on CF-Connecting-IP (backstop for direct-IP hits)
Origin firewall80/443 allowed only from Cloudflare IP ranges; 9944 / 9615 denied
Docker resource limitsmem_limit / cpus (defaults RPC_MEM_LIMIT=6g, RPC_CPUS=3) so a flood can't starve the host

Origin firewall — allow Cloudflare only, deny RPC/metrics:

sudo ufw allow 22/tcp      # SSH
sudo ufw allow 30333/tcp # P2P — public (this node is a bootnode)

# 80/443 only from Cloudflare's edge ranges
for ip in $(curl -s https://www.cloudflare.com/ips-v4); do
sudo ufw allow from "$ip" to any port 443 proto tcp
sudo ufw allow from "$ip" to any port 80 proto tcp
done

sudo ufw deny 9944 # RPC — Caddy reaches the node over loopback
sudo ufw deny 9615 # Prometheus — not public
sudo ufw enable

Caddyfile (origin TLS + per-IP rate limit):

{$RPC_DOMAIN:rpc-1.testnet.orbinum.io} {
tls /etc/caddy/origin.pem /etc/caddy/origin.key

rate_limit {
zone rpc {
key {http.request.header.CF-Connecting-IP}
events 100
window 10s
}
}

reverse_proxy localhost:9944
}

In Cloudflare, set SSL/TLS → Full (Strict), the RPC DNS records to Proxied, and add the WAF skip + rate-limiting rules described in docs/rpc-node.md in node-deploy.

Learn More