Ports & Endpoints
The canonical answer to "which ports do I open?" — it differs by role, and the difference is not cosmetic.
The matrix
| Port | Development | Public RPC | Indexer archive | Validator |
|---|---|---|---|---|
| 30333 P2P | local | public | outbound only | public |
| 9944 RPC | local | loopback, Caddy in front | loopback only | loopback only |
| 9615 metrics | — | host NIC — firewall it | host NIC — firewall it | loopback by default |
| 443 / 80 | — | public, Cloudflare only | — | — |
Only two things face the internet on purpose: P2P on 30333, and HTTPS on 443 for the public RPC. Everything else is either loopback or firewalled to a specific source.
Why the roles differ so much
RPC and indexer nodes run with network_mode: host. There is no port
mapping at all — the values in .env are the sockets the node listens on, and
the firewall is the only thing containing them. Get a ufw rule wrong and
the port is genuinely open.
The validator runs on bridge networking with an explicit ports: block, and
its defaults fail closed:
RPC_BIND=127.0.0.1 # leave this. NEVER 0.0.0.0 or a public IP
METRICS_BIND=127.0.0.1 # forget to set it and nothing is exposed
Those loopback defaults are why the validator's RPC is safe despite the node serving unsafe methods — see Node Flags.
The indexer archive takes no inbound P2P at all. It deliberately sets no
--public-addr, so no peer tries to connect to it; outbound connections still
get their replies. Do not open 30333 inbound for it.
Firewall
Every role:
sudo ufw allow 22/tcp # SSH — restrict to your own IP if you can
sudo ufw allow 30333/tcp # P2P (skip on the indexer archive)
sudo ufw enable
Public RPC adds the Cloudflare-only rules for 80/443 and explicitly denies the node's own ports — Caddy reaches the node over loopback:
sudo ufw deny 9944 # RPC
sudo ufw deny 9615 # Prometheus
node-deploy ships scripts/sync-cloudflare-ufw.sh to load the Cloudflare
ranges and keep them current; run it from cron monthly. It fails closed — if the
range fetch fails it aborts rather than wiping the allow-list and locking
Cloudflare out.
Metrics on a private interface:
sudo ufw allow from <monitoring-ip> to any port 9615 proto tcp
sudo ufw deny 9615
Source-address restriction is the only thing protecting 9615. Never replace it
with a blanket ufw allow.
Endpoints a public RPC serves
| URL | Use |
|---|---|
https://<your-domain> | HTTP JSON-RPC |
wss://<your-domain> | WebSocket — Polkadot.js, Talisman, SubWallet |
Both terminate at Caddy, which proxies to the node on loopback and applies rate limits per client IP.
The Docker image still EXPOSEs it, but nothing uses it. Substrate serves HTTP
and WebSocket on the same --rpc-port.