Skip to main content
RunBook Academy

← All checklists in Observability

Before deploymentprometheus-readiness

Prometheus Production Readiness

29 items ·24 critical ·5 warn ·0 info

Run this before a Prometheus becomes something an on-call rotation depends on, and again after any change to retention, the alerting path, the listener or the service account. Those four are where a change looks like tuning and behaves like an outage.

The items run from configuration through storage, then the delivery path, then the HTTP surface, and end with the two questions that only make sense from outside: who watches this server, and what happens when it is the thing that failed.

Ask the process, not the file

Almost every item here reads the running server rather than the configuration on disk, because those two disagree more often than anyone expects. A reload that failed leaves the previous configuration serving and logs the failure once. A flag changed in the unit file does nothing until a restart, because reload does not re-read flags. The status endpoints are where the process states what it actually loaded, and that answer is the one that matters.

Substitute your own hostnames, paths and job names throughout — the ones below are the shapes this course uses, not an estate that exists.

The two failure modes worth internalising

The first is a full disk. Retention deletes whole blocks and only when a block lies entirely outside the limit, and the size limit budgets compacted blocks alone — the write-ahead log, the head and compaction scratch all sit outside it. When the volume fills, ingestion stalls, and compaction, which is the mechanism that would free space, needs scratch space it no longer has. The recovery path is deadlocked and the server cannot page anybody about it.

The second is a delivery path that has never been executed. Rules can fire correctly into an Alertmanager that was never discovered, or through a relabel rule that drops them, or to a single instance that is down. Each produces no alerts, and no alerts is what a healthy night looks like. That asymmetry is why the watchdog item is a hard requirement rather than a nicety.

Sign-off

  • Reviewer: ________________ Date: ___________
  • Platform owner: ___________ Date: ___________
  • On-call representative: ____ Date: ___________

Critical24 items

  1. promtool check config /etc/prometheus/prometheus.yml
  2. promtool check rules /etc/prometheus/rules/*.yml && promtool test rules /etc/prometheus/tests/*.yml
  3. curl -s http://localhost:9090/metrics | grep -E '^prometheus_config_last_reload'
  4. curl -fsS http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' | grep -A5 external_labels
  5. curl -fsS http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' | grep -E "scrape_interval|scrape_timeout|evaluation_interval"
  6. curl -fsS http://localhost:9090/api/v1/status/flags | jq -r '.data["storage.tsdb.retention.time"], .data["storage.tsdb.retention.size"]'
  7. curl -fsS -G http://localhost:9090/api/v1/query --data-urlencode 'query=prometheus_tsdb_lowest_timestamp' | jq -r '.data.result[0].value[1]'
  8. df -h /var/lib/prometheus; du -sh /var/lib/prometheus
  9. curl -fsS -G http://meta-prom:9090/api/v1/query --data-urlencode 'query=node_filesystem_avail_bytes{mountpoint="/var/lib/prometheus"}' | jq -r '.data.result[] | "\(.metric.instance) \(.value[1])"'
  10. curl -s http://localhost:9090/metrics | grep -E '^prometheus_remote_'
  11. curl -fsS http://localhost:9090/api/v1/alertmanagers | jq '{active: (.data.activeAlertmanagers|length), dropped: (.data.droppedAlertmanagers|length)}'
  12. curl -fsS http://alertmanager.internal:9093/api/v2/status | jq '.cluster'
  13. amtool --alertmanager.url=http://alertmanager.internal:9093 alert add readiness_smoke severity=info
  14. curl -fsS -G http://meta-prom:9090/api/v1/query --data-urlencode 'query=ALERTS{alertname="Watchdog",alertstate="firing"}' | jq '.data.result | length'
  15. curl -fsS -G http://localhost:9090/api/v1/query --data-urlencode 'query=rate(prometheus_rule_group_iterations_missed_total[10m]) > 0' | jq -r '.data.result[] | "\(.metric.rule_group) \(.value[1])"'
  16. curl -fsS -G http://localhost:9090/api/v1/query --data-urlencode 'query=topk(10, scrape_samples_scraped)' | jq -r '.data.result[] | "\(.metric.job) \(.metric.instance) \(.value[1])"'
  17. ss -tlnp | grep 9090
  18. curl -sk -o /dev/null -w "%{http_code}\n" https://prometheus.internal:9090/api/v1/status/config
  19. curl -fsS http://localhost:9090/api/v1/status/flags | jq -r '.data["web.enable-admin-api"], .data["web.enable-lifecycle"]'
  20. grep -nE "^[[:space:]]*(password|bearer_token|credentials):" /etc/prometheus/prometheus.yml /etc/prometheus/*.yml
  21. getent passwd prometheus; ls -l /etc/prometheus/prometheus.yml; ls -ld /var/lib/prometheus
  22. systemctl show prometheus -p ProtectSystem -p ReadWritePaths -p NoNewPrivileges -p CapabilityBoundingSet -p LimitNOFILE
  23. curl -fsS -G http://meta-prom:9090/api/v1/query --data-urlencode 'query=up{job="prometheus"}' | jq -r '.data.result[] | "\(.metric.instance) \(.value[1])"'
  24. curl -fsS -G http://meta-prom:9090/api/v1/query --data-urlencode 'query=count by (replica) (up{job="prometheus"} == 1)' | jq -r '.data.result[] | "\(.metric.replica) \(.value[1])"'

Warning5 items

  1. curl -fsS http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' | grep -A6 remote_write
  2. curl -fsS http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' | grep -A10 write_relabel_configs
  3. curl -fsS http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' | grep -A10 alert_relabel_configs
  4. curl -fsS "http://localhost:9090/api/v1/rules?type=record" | jq -r '.data.groups[].rules[].name'