Which node serves endpoints
Serve endpoints from a dedicated full node, never from a validator. The validator node should keep RPC bound to localhost and connect only to its sentries. See Validator security. An endpoint node needs no consensus key and no stake, only disk, bandwidth, and a public address. Decide first how much history the node keeps, because that decides which queries it can answer:- A node with the default
pruning = "default"answers current-state queries and recent heights. This is enough for wallets and most relayers. - A node with
pruning = "nothing"answers historical queries at any height since it was synced. Explorers and indexers need this. Size and sync options are on Archive nodes.
CometBFT RPC
The CometBFT RPC listener is inconfig.toml under [rpc]. The defaults written by gaiad init bind it to localhost:
laddr on 127.0.0.1. Put the reverse proxy in front of it. Binding 0.0.0.0 exposes the unauthenticated endpoint directly, including /unsafe_* style routes if unsafe = true is ever set. Set cors_allowed_origins = ["*"] only if browser wallets must reach this node directly. A proxy can also add the header.
To change a config.toml key with gaiad config set config ... on gaia v28.3.1, add the -s flag (--skip-validate). Without it the command returns cometbft config is not supported.
Check that the RPC answers and is not catching up:
catching_up field must be false before the node goes behind a public hostname.
Transaction indexer
Wallets and explorers call/tx?hash= and /tx_search. Both need the transaction indexer, which is on by default:
indexer = "null" only on nodes that will never serve transaction lookups (sentries, signing nodes). Switching the indexer later does not index old blocks. The node has to resync or restore a snapshot that was made with indexing enabled.
REST (API) and gRPC
The SDK listeners are inapp.toml. The API is off by default. gRPC is on, bound to localhost:9090:
gaiad config set app command accepts dotted keys:
/cosmos/... paths, for example:
cosmoshub-4. gRPC needs a Transport Layer Security (TLS) terminating proxy that supports HTTP/2. gRPC-web shares the API port.
Reverse proxy and limits
Put TLS termination, request limits, and a health check in a reverse proxy (nginx, Caddy, HAProxy, or a cloud load balancer). The settings that matter, whatever the proxy:- Health check on
GET /statusand mark the backend down whencatching_upistrueor the latest block time is older than about a minute. A node that is behind still returns 200. - Per-client rate limit on
/tx_search,/block_search, and/abci_query. These are the calls that let one client saturate disk. - Websocket pass-through on
/websocketfor Hermesevent_source = { mode = "push" }subscriptions, with an idle timeout longer than the relayer’s ping interval. - A body size limit around 1 MiB. Transactions on the Hub are limited by the consensus
max_bytesof 2,000,000, so nothing legitimate is larger.
Publish the endpoint
Other operators find public endpoints through the chain registry (apis.rpc, apis.rest, apis.grpc) and through cosmos.directory, which reads the registry. Add the hostname with a pull request against the registry once the endpoint has been stable for a while. Remove it again before taking the node down for a long time. Stale entries break wallets for everyone.
Verify
From a machine outside the node’s network, with the public hostname:false. The second returns the gov module account’s uatom balance, which proves abci_query works through the proxy. The third returns height 33424479, code 0, which proves the transaction indexer and /tx work. On a pruned node that was synced after that height the third call fails, which is expected.
Next
- Snapshots to publish snapshots from the same node.
- Peering to add seeds and persistent peers.
- Relaying to point a relayer at this endpoint.
- Monitoring for Prometheus metrics on port 26660.