Enable the CometBFT metrics endpoint
config.toml key is prometheus in the [instrumentation] table (default false). The listener is prometheus_listen_addr = ":26660". The metric namespace is cometbft, so every metric below is exported as cometbft_<subsystem>_<name>. On gaia v28.3.1 gaiad config set config needs --skip-validate. Confirm with gaiad config get config instrumentation.prometheus, then restart the service.
cometbft_consensus_height{chain_id="cosmoshub-4"} <height>. The endpoint binds all interfaces by default. Firewall 26660 to your Prometheus host, or set prometheus_listen_addr to a private address.
Application metrics (bank, staking, Cosmos SDK telemetry) are separate and optional. They need gaiad config set app telemetry.enabled true, gaiad config set app telemetry.prometheus-retention-time 60, and gaiad config set app api.enable true. They are served by the API server at http://localhost:1317/metrics?format=prometheus. Keep the API bound to localhost on a validator.
Metrics that matter for a validator
All names have thecometbft_ prefix and a chain_id label. The full list is in the CometBFT metrics reference.
Add node_exporter on every host for disk, memory, and clock drift. A full disk is the most common reason a healthy validator stops.
Scrape and alert
A minimal Prometheus scrape config:ValidatorNotSigning rule needs ignoring(validator_address) because cometbft_consensus_validator_last_signed_height has a validator_address label that cometbft_consensus_height does not. Without it the subtraction returns nothing. The VotingPowerZero rule tests for an absent gauge instead of == 0 because CometBFT stops exporting cometbft_consensus_validator_power (and validator_last_signed_height) when the validator leaves the set. As a result, == 0 never matches. For the same reason ValidatorNotSigning only covers a node that is up and still in the set. The HeightStalled rule firing on the validator alone means your node stopped. Firing on validator and sentries together, it means the chain halted (an upgrade height, usually). Route page alerts to something that wakes you. See the Prometheus alerting rules and Alertmanager docs for routing and silences, and Grafana for dashboards.
A second signal from outside
Metrics from the validator cannot tell you the validator is down when the whole host is gone. Nor canup == 0 tell you whether you are also being slashed. From a machine that is not part of the validator setup, poll the chain:
missed_blocks_counter field is the chain’s own count inside the current 10,000-block window. Alert when it rises over a few minutes. A jailed_until in the future means you are already jailed. Get your cosmosvalcons address with gaiad comet show-address on the host that holds the key, or from gaiad query staking validator <cosmosvaloper> (consensus_pubkey). Many operators also use a third-party validator alerting bot for the same data. Treat it as the third signal, not the first.
Logs with journald
A systemd service (Run gaiad as a service) sends gaiad output to journald. The commands name the plaingaiad unit. Use -u cosmovisor if the node runs under the Cosmovisor unit.
/etc/systemd/journald.conf, then systemctl restart systemd-journald:
journalctl --disk-usage command shows what the journal holds. The journalctl --vacuum-size=2G command trims it immediately. See journald.conf(5) and journalctl(1).
Log volume is set in config.toml. The default is log_level = "info". The value *:error,consensus:info,p2p:error cuts noise while keeping what you need. Use log_format = "plain", or "json" for a log shipper. Set them with gaiad config set config log_level "<value>" --skip-validate.
After every restart
Run these in order each time gaiad or the signer restarts, before you walk away:- Run
curl -s localhost:26657/status | jq '.result.sync_info.catching_up'. It printsfalse. - Run
curl -s localhost:26657/status | jq '.result.validator_info'. It shows the expectedpub_keyand a non-zerovoting_power. - The signer’s state file (or
data/priv_validator_state.jsonfor a local key) shows aheightthat increases every few seconds. - Run
curl -s localhost:26660/metrics | grep -E '^cometbft_consensus_(height|validator_last_signed_height)[{ ]'. The two values are within a block or two of each other. - Run
gaiad query slashing signing-info <cosmosvalcons> --output json | jq .val_signing_info.missed_blocks_counter. It printsnull(the JSON output omits zero fields) or a number that stops increasing. - Run
journalctl -u gaiad --since "5 minutes ago" | grep -ciE 'panic|found signature from the same key'. It prints0. An exit withfound signature from the same key(thedouble_sign_check_heightcheck) means the key signed recently somewhere. Stop and find out where before starting again.
Verify
- Prometheus shows the validator and sentry targets as
UP. - Stopping the node on the testnet fires
NodeDownwithin two minutes, and the chain-sidemissed_blocks_counterrises. TheValidatorMissedBlocksalert fires once the node is back, because the node counts the blocks it was expected to sign while it catches up. Stopping only the signer, with the node up and in the set, is the caseValidatorNotSigningis written for. Starting everything again clears the alerts. journalctl --disk-usagestays under the limit you set after a week.