Metrics Reference
This document describes all Prometheus metrics exposed by OmniUPF on the /api/metrics endpoint (the HTTP API runs on api_port, default 8080). Note the /api prefix - every API route is served under it.
All metrics are declared in lib/upf_ex/prometheus_metrics.ex. Metric names, types, and labels below are taken directly from that source; if the code changes, update this file to match.
Metric Categories
- PFCP metrics - control-plane message, association, session, and error counters
- Session lifecycle metrics - session create/delete/modify counters and establishment latency
- Packet buffering metrics - buffer state and throughput for idle-UE downlink buffering
- Downlink Data Report (DLDR) metrics - PFCP Session Report Request notifications and FAR index tracking
- GTP-U metrics - echo responses and Error Indication counters
- URR metrics - usage-report counters and active URR timers
- eBPF map metrics - dataplane map entry gauges
- Dataplane throughput - per-direction (N3/N6) counters, on-demand via
/api/v1/xdp_statsonly (not a scrape metric — see #57) - Resource / routing metrics - TEID/FAR-ID pool and UE route gauges
- Walled Garden metrics - captive-portal interception, forwarding, and DNS spoofing counters
PFCP metrics
Control-plane protocol counters between the UPF and its PFCP peers (SMF / PGW-C / SGW-C).
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_pfcp_associations | Gauge | none | Currently active PFCP associations (all peers) |
upf_pfcp_sessions | Gauge | none | Currently active PFCP sessions (all peers) |
upf_pfcp_peer_restarts_total | Counter | peer | Peer restart detections (Recovery Timestamp change) |
upf_pfcp_duplicate_requests_total | Counter | none | Duplicate PFCP requests detected and suppressed |
upf_orphaned_sessions_deleted_total | Counter | none | Sessions deleted as a result of a peer restart |
upf_pfcp_messages_rx_total | Counter | type, peer | PFCP messages received per message type and peer |
upf_pfcp_messages_tx_total | Counter | type, peer | PFCP messages sent per message type and peer |
upf_pfcp_errors_total | Counter | type | PFCP request rejections, labelled by the reject cause (e.g. session_context_not_found, mandatory_ie_missing) |
Session lifecycle metrics
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_sessions_created_total | Counter | none | PFCP sessions established |
upf_sessions_deleted_total | Counter | none | PFCP sessions deleted |
upf_sessions_modified_total | Counter | none | PFCP sessions modified |
upf_session_establishment_duration_microseconds | Histogram | none | Session establishment processing time (microseconds). Buckets: 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000 |
Packet buffering metrics
The UPF buffers downlink packets when a UE is idle, holding them until the UE is paged and transitions to connected. Packets arrive from the eBPF dataplane on the buffer UDP port (default 22152).
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_buffer_packets_received_total | Counter | none | Packets received on the buffer port from the dataplane |
upf_buffer_packets_buffered_total | Counter | none | Packets successfully added to the buffer |
upf_buffer_packets_flushed_total | Counter | none | Packets flushed from the buffer |
upf_buffer_packets_dropped_total | Counter | reason | Packets dropped from / not admitted to the buffer |
upf_buffer_packets_current | Gauge | none | Packets currently held in the buffer |
upf_buffer_flush_operations_total | Counter | none | Successful buffer flush operations (per FAR flush) |
upf_buffer_flush_packets_sent_total | Counter | none | Packets sent during flush operations |
upf_buffer_flush_errors_total | Counter | reason | Failed buffer flush operations |
upf_buffer_packets_dropped_total reason values (from buffer-add drops and buffer-listener errors):
global_limit- total buffer capacity (buffer_max_total) reachedfar_limit- per-FAR buffer limit (buffer_max_per_far) reachedexpired- TTL (buffer_ttl_ms) exceeded before flushread_error- error reading from the buffer sockettoo_small- packet too small for a GTP headerinvalid_gtp_type- non-G-PDU GTP message typeunknown_teid- no PDR/FAR found for the TEIDnot_buffering_far- FAR does not have a BUFF action
upf_buffer_flush_errors_total reason values:
far_lookup_failed- could not look up FAR info from the eBPF mapno_forw_action- FAR does not have a FORW action setconnection_failed- failed to open a UDP socket for flushing
Downlink Data Report (DLDR) metrics
Metrics for PFCP Session Report Request notifications sent to the control plane when downlink packets are buffered. These notifications trigger the SMF/PGW-C to page the UE.
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_dldr_sent_total | Counter | peer | Downlink Data Reports sent, labelled by SMF/PGW-C address |
upf_dldr_errors_total | Counter | none | DLDR encoding / send errors |
upf_dldr_skipped_total | Counter | none | DLDRs skipped because the FAR was already notified |
upf_far_index_size | Gauge | none | FARs registered in the FarIndex for DLDR notification |
GTP-U metrics
GTP-U path-management and Error Indication counters. Error Indications are exchanged when a peer receives packets for an unknown TEID (typically after a restart).
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_gtpu_echo_responses_tx_total | Counter | none | GTP-U Echo Responses sent |
upf_gtpu_error_indications_sent_total | Counter | none | GTP-U Error Indications sent (for unknown incoming TEIDs) |
upf_gtpu_error_indications_rx_total | Counter | none | GTP-U Error Indications received from peers |
upf_error_indication_sessions_deleted_total | Counter | none | Sessions deleted as a result of a received Error Indication |
When Error Indications are sent: the UPF receives a GTP-U packet for a TEID that does not exist (e.g. after a restart, or the session was already deleted), and informs the sender to stop.
When Error Indications are received: a downstream peer does not recognise a TEID the UPF forwarded to; the UPF deletes the affected session to stop forwarding to a dead tunnel.
URR (Usage Reporting Rule) metrics
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_urr_reports_sent_total | Counter | trigger | URR usage reports sent, labelled by reporting trigger |
upf_urr_report_errors_total | Counter | none | URR report encoding / send errors |
upf_urr_active_timers | Gauge | none | Active URR timers (periodic / time-threshold / quota-validity) |
trigger reflects the URR reporting trigger that fired - e.g. periodic (PERIO), volume_threshold (VOLTH), time_threshold (TIMTH), quota_validity (QUVTI).
Per-URR byte counters are not exported as Prometheus metrics (to avoid high cardinality). Read individual URR statistics via the REST API at /api/v1/urr_map.
eBPF map metrics
Gauges for dataplane map utilisation, refreshed on each scrape.
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_ebpf_far_entries | Gauge | none | FAR entries in the eBPF map |
upf_ebpf_pdr_uplink_entries | Gauge | none | Uplink PDR entries in the eBPF map (PDRs with a TEID) |
upf_ebpf_pdr_downlink_entries | Gauge | none | Downlink PDR entries in the eBPF map (PDRs keyed by UE IP) |
upf_ebpf_far_entries mirrors the allocated-FAR-id pool (upf_far_ids_allocated), since the far_map holds one entry per allocated FAR id. The PDR gauges are aggregated from live session state on each scrape (cost is O(total sessions)).
Dataplane throughput (on-demand only — not a Prometheus scrape metric)
Per-direction packet and byte counters for user-plane traffic (N3 = GTP-U side towards the gNB/eNB; N6 = SGi side towards the data network) come from the eBPF upf_ext_stat percpu map.
Removed from the Prometheus scrape (issue #57). These were previously exported as
upf_n3_*/upf_n6_*gauges, populated on every/api/metricsscrape by abpf_map_lookup_elemon theupf_ext_statmap — the same per-CPU map the XDP datapath writes on every packet. That per-scrape read was proven to trigger avirtio_netgeneric-XDP TX-ring wedge (with the scrape blocked, the exact breaking load ran 30/30 cycles with zeroNETDEV WATCHDOG). To keep the metrics path from ever touching the datapath, these gauges are no longer collected or exported.
How to read live N3/N6 counters now: GET /api/v1/xdp_stats returns the same fields as JSON, and only touches eBPF when you explicitly call it (not on a timer, not on a Prometheus scrape). Fields: rx_n3 / tx_n3 / rx_n6 / tx_n6 (packets) and rx_n3_bytes / tx_n3_bytes / rx_n6_bytes / tx_n6_bytes.
Durable per-direction throughput in Prometheus should instead come from the substrate fix that lets the datapath be observed safely (see #57), or from an external counter (e.g. the host/tap byte counters).
Resource / routing metrics
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_routes_total | Gauge | none | UE IP routes currently tracked by the route manager |
upf_teid_allocated | Gauge | none | TEIDs currently allocated from the pool |
upf_far_ids_allocated | Gauge | none | FAR IDs currently allocated from the pool |
Walled Garden metrics
Counters for the out-of-credit / captive-portal walled garden. See the Walled Garden Guide for behaviour.
| Metric Name | Type | Labels | Description |
|---|---|---|---|
upf_walled_garden_active_redirects | Gauge | none | Active walled-garden redirect sessions |
upf_walled_garden_packets_intercepted_total | Counter | none | Packets intercepted by the walled garden |
upf_walled_garden_packets_dropped_total | Counter | none | Packets dropped by the walled garden |
upf_walled_garden_packets_forwarded_total | Counter | dst_ip | Whitelisted packets forwarded, per destination IP |
upf_walled_garden_bytes_forwarded_total | Counter | dst_ip | Whitelisted bytes forwarded, per destination IP |
upf_walled_garden_dns_spoofed_total | Counter | domain | DNS queries spoofed to the portal, per domain |
upf_walled_garden_dns_forwarded_total | Counter | domain | DNS queries forwarded (whitelisted domains), per domain |
Cardinality note. The
dst_ipanddomainlabels are unbounded. Keep the whitelist tight, and aggregate away these labels in recording rules if you scrape a busy walled garden.
Using Prometheus Metrics
Accessing Metrics
Metrics are exposed on the /api/metrics endpoint of the HTTP API server, which listens on api_port (default 8080). Note the /api prefix - every API route is served under it:
# View raw metrics
curl http://localhost:8080/api/metrics
# Example output
upf_pfcp_sessions 42
upf_pfcp_associations 2
upf_dldr_sent_total{peer="10.100.50.241"} 17
Prometheus Configuration
Add the OmniUPF target to your prometheus.yml:
scrape_configs:
- job_name: 'omniupf'
metrics_path: /api/metrics
static_configs:
- targets: ['localhost:8080']
Grafana Dashboards
Useful panels to build from these metrics:
- Session and association counts (
upf_pfcp_sessions,upf_pfcp_associations) - Per-direction N3/N6 throughput is available on demand via
/api/v1/xdp_stats(removed from the scrape — see #57) - Buffer pressure (
upf_buffer_packets_current, drop rate byreason) - DLDR / paging activity (
rate(upf_dldr_sent_total[5m])) - Error Indication and peer-restart rates for tunnel-health alerting
Related Documentation
- Monitoring Guide - Statistics monitoring, capacity planning, and alerting
- Configuration Guide - Configure
api_port, buffer tuning, and other UPF options - Web UI Guide - View metrics in the Statistics page
- Architecture Guide - eBPF datapath and performance optimization
- Rules Management Guide - Understanding PDR, FAR, QER, URR metrics
- Walled Garden Guide - Captive-portal redirect and whitelist behaviour
- Troubleshooting Guide - Using metrics for diagnostics