ObservabilityIII · Metrics FundamentalsLabels and series
Labels, Dimensions, and Time Series
What you'll learn
- Identify the parts of a Prometheus time series
- Apply the rules for safe label design
- Recognise the operational meaning of dimensions
- Read a time series as a sampled observation
Prerequisites
Verified against Prometheus 2.55.x · Alertmanager 0.28.x · node_exporter 1.8.x · blackbox_exporter 0.26.x · Grafana 11.x · Loki 3.x · Tempo current · OpenTelemetry Collector 0.110.x · Grafana Alloy current · Docker Engine 28.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-13
A time series is the unique combination of metric name and label set. Adding a label with N values multiplies the series count by N. The label design is the discipline that keeps Prometheus memory and storage predictable.
This lesson covers the parts of a time series, the rules for safe label design, and the operational meaning of each label.
Anatomy of a time series
http_requests_total{method=GET, route=/checkout, status=200} 4096 @1700000000
| | | | |
| +-- label set | + sample + timestamp
+-- metric name (value)
Five parts:
- Metric name. The Prometheus convention is
<unit>_<noun>_<verb>. Examples:http_requests_total,node_memory_MemAvailable_bytes,http_request_duration_seconds_bucket. - Label set. Zero or more key-value pairs. Order does not matter; the set is the same regardless of order.
- Value. The sample. A counter is an integer; a gauge is float; a histogram has multiple components.
- Timestamp. The scrape-time (or push-time, for Pushgateway) millisecond-precision timestamp.
- Metadata.
Help,unit, optional labels likele="..."for histograms.
How labels become series
Adding a label multiplies the series count:
http_requests_total — 1 series
http_requests_total{method="GET"} — 1 series
http_requests_total{method="GET",status="200"} — 1 series
http_requests_total{method="POST",status="500"} — 1 series
http_requests_total{method="POST",status="200"} — 1 series
http_requests_total{method="POST",status="500"} — 1 series
= 5 total
Five label combinations = five time series. Each takes memory to index and disk to store. The metric itself has 5 series; the same metric with three status codes and three methods might have 9; with 50 status codes, 150.
The cardinality design decision is a memory and storage decision: how many distinct combinations of labels do you want the platform to track?
Label design rules
Five rules:
- Distinct operations. Each label should identify a distinct operation, environment, host, instance, route, or status code. Labels are for aggregation, not for tagging arbitrary data.
- Bounded cardinality. The number of distinct values for each label must be small and known. “User ID” is unbounded and dangerous. “Route” with 30 distinct paths is bounded and useful.
- Operational alignment. Labels should map to operational dimensions: route, instance, environment, region. The label set is the answer to “how will I pivot in a dashboard?”
- Coherence. All series of one metric should share most
labels. A
requests_totalandrequest_duration_secondswith mismatched label sets cannot be joined cleanly. - No secrets. Labels are visible in URLs (
/api/v1/query) and may be exposed. Do not label with secrets, PII, or internal hostnames that should not be public.
What labels map to operationally
The label set is the dashboard’s pivot dimensions. A good label set is the answer to “in an incident, what will I want to filter the metric by?”
Common operational labels:
- route. HTTP endpoint. Tells you which path the user hit. Cardinality should be bounded by the API design.
- method. HTTP verb. Tells you GET vs POST vs DELETE. Bounded by HTTP.
- status. HTTP status code class. Bounded by HTTP.
- instance. The producer of the metric (host, container, pod). Bounded by fleet size.
- region. Cloud region. Bounded by deployment.
- env. Production, staging, dev. Bounded to ~3.
- version. Application version. Bounded by deployment frequency.
- dependency. The downstream service. Useful for tracing.
A label like user_id, request_id, session_id, UUID is
unbounded — adding a new label like this typically explodes
the series count by orders of magnitude. Part IV on cardinality
covers this in production depth.
How many series is too many?
A small Prometheus can comfortably hold ~1 million active series. A medium one can hold 10 million. The label design budget per metric is a design-time choice:
- Node metrics. ~5000 series across ~1000 hosts × ~5 collectors with a few labels each.
- Application request metrics. Bounded by route × status × method × instance. ~10000 series for a mid-size service.
- Histograms. × bucket count — a 12-bucket histogram produces 12 + 2 series.
- Sum across the platform. Depends on fleet size; design the label budget in advance.
A team that has not designed the label budget discovers it during the cardinality incident, not before.
Operational reading
The label set is what the operator uses to pivot the metric in the dashboard. The label set is what the alert fires on. The label set is what the incident response uses to bound the search.
A good label design:
- Always has
instance(so the operator can find which host is affected). - Always has
route(so the operator can find which endpoint is affected). - Has
version(so the operator can bound the search to a recent deploy). - Has bounded cardinality.
A bad label design:
- Has
user_id(operator cannot filter; cardinality explodes). - Has
request_id(operator cannot reuse the dashboard; every request becomes a new series). - Has
error_message(operator cannot read; metric is cardinality-bloated and unreadable).
The label design is the dashboard design. The two are inseparable.
Production guidance
- Design the label budget at metric instrumentation time. Discuss every new label in a code review.
- Test for cardinality before deploying a new label. Use the
tsdbtool orpromtoolto estimate series count. - Use recording rules to compute aggregations that would otherwise consume excessive series (e.g. sum the per-route counter into a fleet-wide counter).
- Document the label set in the instrumentation guide.
- Avoid labels with high cardinality or unbounded values.
Verification
You should be able to answer:
- What are the parts of a Prometheus time series?
- How do labels multiply series count?
- Why are high-cardinality labels dangerous?
- What does a “good” label design look like?
Quiz
Knowledge check · 8 questions
Q1. What is the primary purpose of labels, dimensions, and time series?
Q2. Which failure mode of labels, dimensions, and time series is most operationally costly?
Q3. Production verification should run on production hosts.
Q4. First response when labels, dimensions, and time series misbehaves?
Q5. Name one signal that confirms labels, dimensions, and time series is healthy.
Q6. Which of these are validation steps?
Q7. Right discipline when changing in production?
Q8. Telemetry usefulness requires:
Passing score: 75%. Answers are checked in this browser.