Monitoring API Gateways with Prometheus and Grafana: Metrics, Dashboards, and Alerts
API7.ai
October 29, 2026
Prometheus and Grafana can turn an API gateway into a useful observation point for request rate, client-visible errors, latency, saturation, and upstream health. A dependable setup does more than expose /metrics: it keeps the scrape path private, controls label cardinality, separates gateway-generated responses from upstream responses, and connects dashboards and alerts to user-visible service objectives.
Metrics show aggregate behavior. They do not prove that an application returned the correct result, identify a caller unless authentication already did so, or replace logs and traces during an investigation.
Key Takeaways
- Scrape a dedicated, private metrics listener instead of publishing operational data on the public API listener by default.
- Start with rate, errors, duration, saturation, and upstream health; add labels only for an operational question.
- Use stable Route and Service identifiers, and treat names as mutable display data.
- Separate client-visible status, upstream status, and the component that generated the response.
- Build Grafana dashboards for investigation and alerts for action; dashboard variables are not available when Grafana evaluates alert rules.
- Test missing metrics, scraper failure, restarts, low traffic, and partial fleet loss before trusting an alert.
Design the Monitoring Path
flowchart LR
C[Clients] --> G[APISIX data plane]
G --> U[Upstream services]
P[Prometheus on operator network] -->|scrape private endpoint| E[APISIX metrics exporter]
E -. local gateway metrics .-> G
P --> R[Recording and alert rules]
P --> D[Grafana data source]
D --> B[Dashboards]
R --> A[Alertmanager or Grafana Alerting]
A --> O[On-call owner and runbook]
The scrape path is an operational control plane. It can reveal Route IDs, Service IDs, upstream addresses, Consumers, hosts, traffic volumes, and failure patterns. Put it on a restricted network, authenticate or tunnel where the environment requires it, and limit who can query Prometheus and Grafana.
Do not infer end-user identity from a consumer, address, or model label alone. The label describes what APISIX associated with a request; the authentication method and trusted proxy configuration determine whether that association is authoritative.
Configure the APISIX 3.19 Exporter
Apache APISIX 3.19.0's prometheus plugin exports a text-format metrics endpoint. Its default static configuration enables a dedicated exporter on 127.0.0.1:9091 at /apisix/prometheus/metrics.
This config.yaml excerpt makes that choice explicit and binds the exporter to a private address placeholder:
plugin_attr: prometheus: export_uri: /apisix/prometheus/metrics metric_prefix: apisix_ enable_export_server: true export_addr: ip: 10.0.8.21 port: 9091
Replace the address with a real private interface and enforce network policy before reloading APISIX. With enable_export_server: true—the default—the dedicated exporter listens at export_addr. With false, that exporter does not listen. APISIX documents an alternative using public-api on the normal request listener, but that places metrics on the public API path and adds request-path exposure and overhead; use it only with an explicit access design.
Enable the plugin on the intended Route, Service, or Global Rule according to the required scope. This Route fragment is illustrative:
{ "id": "orders-read", "uri": "/orders/*", "plugins": { "prometheus": { "prefer_name": false } }, "upstream_id": "orders-read" }
prefer_name defaults to false: when the relevant Route or Service exists, APISIX selects its ID for that label. With true, APISIX selects its configured name, so provide names before depending on this path; do not assume that a missing name automatically falls back to an ID. These labels also have absence paths. An unmatched request has an empty Route label, and for the built-in HTTP status, latency, and bandwidth metrics, APISIX uses the configured Route host as the Service label when no Service exists. The example above has an upstream_id but no Service, so its Service label is not a Service ID or name. IDs are usually safer for joins and history because display names can change; names can improve readability only when the organization governs their uniqueness and lifecycle. Neither Boolean branch changes authentication or makes a label trusted identity.
Scrape from Prometheus
The following Prometheus fragment is runnable after replacing the target and making the private address reachable from Prometheus:
scrape_configs: - job_name: apisix-gateway metrics_path: /apisix/prometheus/metrics static_configs: - targets: - 10.0.8.21:9091 labels: environment: production gateway_cluster: edge-eu-1
Use service discovery rather than a static target for an elastic fleet, but keep environment and gateway_cluster values bounded. Verify up for every expected target and alert on a missing fraction of the fleet; one healthy exporter does not prove that all gateway instances are being scraped.
Choose Metrics That Answer Operating Questions
APISIX 3.19 provides, among other metrics:
| Question | APISIX metric or label |
|---|---|
| What status did the client receive? | apisix_http_status and code |
| Who generated the response? | response_source: apisix, nginx, or upstream |
| How long did the request take? | apisix_http_latency{type="request"} |
| How much time was upstream waiting? | apisix_http_latency{type="upstream"} |
| What was gateway overhead? | apisix_http_latency{type="apisix"} |
| Are active connections approaching capacity? | apisix_nginx_http_current_connections |
| Is the configuration store reachable? | apisix_etcd_reachable |
| Which checked upstream nodes are healthy? | apisix_upstream_status |
| Is metrics storage running out of space? | apisix_shared_dict_capacity_bytes and apisix_shared_dict_free_space_bytes |
Client-visible status can differ from upstream status after plugins and proxy error handling. A gateway-generated 429, an NGINX 502, and an upstream 503 require different owners even if all appear as unsuccessful requests.
Control Cardinality Before It Controls You
Prometheus instrumentation guidance warns that every label set creates another time series with memory, CPU, disk, and network cost. Never use request IDs, raw URLs, user IDs, email addresses, tokens, or other unbounded values as metric labels.
Before adding a label, document:
- the operational question it answers;
- the maximum number of values per environment;
- who owns stale values;
- the expected series multiplication;
- whether logs or traces are a better tool.
APISIX 3.19 Plugin Metadata can collapse selected built-in labels to an empty string while preserving the metric schema. Use that control when per-node, per-consumer, or per-model detail is not required. Do not add upstream_addr merely because it is available; elastic node churn can create long-lived series.
Build Recording Queries First
These PromQL expressions are query examples, not universal alert thresholds. Validate them against the labels present in your deployment.
Client-visible 5xx ratio over five minutes:
sum(rate(apisix_http_status{code=~"5.."}[5m])) / sum(rate(apisix_http_status[5m]))
Per-Route p95 request latency in milliseconds:
histogram_quantile( 0.95, sum by (le, route) ( rate(apisix_http_latency_bucket{type="request"}[5m]) ) )
Fraction of free space in APISIX's prometheus-metrics shared dictionary:
apisix_shared_dict_free_space_bytes{name="prometheus-metrics"} / apisix_shared_dict_capacity_bytes{name="prometheus-metrics"}
Record expensive or frequently reused expressions. Keep units in panel titles and alert annotations: APISIX HTTP latency histograms are in milliseconds, while many application and Prometheus ecosystem duration metrics use seconds.
Design Grafana Dashboards for Questions
Grafana's Prometheus data source supports PromQL, alerting, recording rules, and exemplars. A useful gateway dashboard has layers:
- Service overview: request rate, success/error ratio, p50/p95/p99 latency, rejected traffic, and SLO budget consumption.
- Gateway diagnosis: response source, APISIX versus upstream latency, active connections, shared-dictionary capacity, config-store reachability, and fleet scrape coverage.
- Upstream diagnosis: health-check status, per-Service latency/error change, and node distribution where cardinality is bounded.
- Change context: deployment, configuration revision, and incident annotations from a separate trusted source.
Store dashboard and data-source provisioning in version control. Grafana permits UI edits to provisioned dashboards, but those edits are not automatically written back to the provisioning source. Decide whether Git or the UI is authoritative to avoid silent drift.
Alert on User Pain and Required Action
Prometheus alerting guidance recommends symptom-based alerts and avoiding pages with no action. Page on sustained user-visible error or latency budget burn; route capacity and component signals to investigation or ticket workflows unless they predict imminent user harm.
Each alert should include:
- the affected service, environment, and gateway cluster;
- the user-visible symptom and observation window;
- the SLO or capacity threshold being consumed;
- a dashboard link that does not depend on unresolved variables;
- a runbook and owning team;
- suppression and recovery expectations.
Grafana evaluates alert rules on the backend without dashboard variable context. A panel query using $route or $cluster must become a rule with explicit label matchers or a recording rule. Test the rule independently from the dashboard.
Validate the Failure Paths
Before production paging, test:
- Prometheus cannot reach one gateway exporter;
- the entire scrape job disappears;
- APISIX restarts and counters reset;
- one Route has no traffic and therefore no series yet;
- the upstream returns errors while APISIX still answers other Routes;
- APISIX generates a rejection before proxying;
- label cardinality grows during an autoscaling or model rollout;
- Grafana is unavailable while Prometheus alert evaluation continues, or vice versa.
Use absent() and fleet coverage carefully: an absent per-Route series can mean no traffic, disabled instrumentation, configuration drift, or a failed scrape. Corroborate before paging.
Monitoring Checklist
- Is the metrics endpoint reachable only from the intended operator network?
- Is
enable_export_serverexplicitly understood for bothtrueandfalse? - Are Route/Service IDs or names chosen deliberately through
prefer_name? - Are unbounded and unnecessary labels prohibited or collapsed?
- Can dashboards separate APISIX, NGINX, and upstream response sources?
- Are latency units and histogram buckets appropriate for the SLO?
- Are alerts evaluated without dashboard-only variables?
- Does every page identify user impact, owner, runbook, and action?
- Are scrape loss, missing series, counter reset, and partial fleet failure tested?
- Are logs and traces available for questions metrics cannot answer?
FAQ
Should the APISIX metrics endpoint be public?
Usually no. It exposes operational detail and can add work to the public request path. Prefer the dedicated exporter on a private operator network.
Should dashboards group by Route name or Route ID?
IDs are the APISIX default and are normally more stable. Use names only when their uniqueness and rename lifecycle are governed. Keep a separate inventory to map IDs to human ownership.
Can Prometheus metrics replace gateway logs?
No. Metrics reveal aggregate trends. Logs and traces provide request-level context, while application checks are needed for semantic correctness.
Next Steps
Use these signals in API gateway reliability engineering, correlate incidents through gateway log analytics, and keep long-term records within the log storage architecture.