High Cardinality: Why Observability Costs Explode and How to Control It
Your best debugging sessions often end with a label: the tenant or pod that turns a vague symptom into a specific fix. Every one of those labels multiplies the number of time series your observability backend has to store, and the same dimension that speeds investigation can quietly raise your bill.
This guide covers what high cardinality means in observability data, how it drives up costs across backend performance and vendor pricing, and how to control it without dropping the labels your team needs during incidents.
What is High Cardinality?
High cardinality describes a metric whose label values combine into more unique time series than a backend can efficiently index and query. Metric cardinality is the number of unique combinations of a metric name and its label values, and every unique combination creates a separate time series your backend must store in an index and scan during queries. Label combinations multiply fast: a request counter with three environments, five services, and 10 status codes produces 150 time series, and adding a user_id label with 1 million users pushes that same metric to 150 million series. Classic offenders include unbounded dimensions such as user identifiers (IDs), session IDs, request and trace IDs, and Kubernetes pod names.
Cardinality vs. Dimensionality
Dimensionality refers to the number of distinct label keys attached to a metric, such as environment, service, and status_code, while cardinality refers to the number of unique value combinations those keys produce together. A metric with low dimensionality can still reach high cardinality if just one label carries a large or unbounded set of values, the way a single user_id key turns a three-key metric into millions of series. Teams that only count how many labels they’ve added can miss this risk entirely, because the danger comes from how many values each label can take, not how many labels exist.
Cardinality vs. Data Volume
Cardinality and data volume are different cost drivers, and conflating them leads teams to cut the wrong thing. Volume measures bytes ingested and grows roughly linearly with traffic. Cardinality measures unique series and grows multiplicatively with each label you add. It increases query time while increasing index size and memory footprint independently of raw data volume. A user_id label adds little to each sample’s byte count, yet it can multiply your series count by six orders of magnitude, which is why a bill can climb while traffic stays flat.
Benefits of High Cardinality
High cardinality isn’t purely a cost problem, since the right labels give your team precision most low-cardinality metrics can’t match. The benefits typically outweigh the cost when the labels map directly to how you triage incidents:
- Faster root-cause isolation: A tenant or pod label lets you filter a spike down to the exact customer or instance behind it, cutting time off an investigation that would otherwise require cross-referencing multiple dashboards.
- Precise, per-dimension alerting: High-cardinality labels let you set thresholds and burn-rate alerts per customer, region, or service instead of one blunt threshold for your entire fleet.
- Business-context correlation: Labels like customer ID or transaction ID connect technical symptoms directly to revenue-impacting accounts, so an incident review can answer who was affected without a separate query against a different system.
These benefits depend entirely on keeping the labels queryable, which is why the goal is managing cardinality rather than eliminating it outright.
Challenges of High Cardinality
The same labels that sharpen debugging create real operational costs once they multiply past what your backend can efficiently handle. Common challenges include:
- Rising storage and memory costs: Every unique series consumes index space and memory before anyone runs a query, so cost climbs even when traffic stays flat.
- Slower queries during incidents: Backends scan the index for matching series before touching sample data, and that scan gets slower as series counts grow, often right when your team needs answers fastest.
- Operational complexity: Tracking which labels are safe to keep and which ones are quietly multiplying requires ongoing governance, not a one-time cleanup.
Weighing these tradeoffs deliberately, rather than reacting after the bill arrives, is what separates teams that keep their debugging context from teams that lose it.
How High Cardinality Happens
Kubernetes generates high-cardinality labels by design. Pod names carry a hash suffix that changes on every restart, and node rotation during cluster scaling adds more churn, so every autoscaling event and deploy mints a new series even when your instrumentation never changes. Large Prometheus deployments can average about 5 million time series per instance, with the largest instances storing about 30 million series each. This time-series database (TSDB) behavior increases cost because histogram metrics multiply every label combination into many bucket series behind the scenes.
Business-context labels add a second layer on top of infrastructure churn. Customer IDs and transaction IDs are exactly the dimensions product teams want on dashboards, and they’re unbounded by definition. Auto-instrumentation often adds labels your team never explicitly chose, and OpenTelemetry (OTel) resource attributes are a common source since a handful of them can combine into billions of possible series. Kubernetes platforms inject pod and node tags, and service meshes and cloud providers add zone and version tags on top of whatever your application defines.
How Cardinality Explosions Drive Up Observability Bills
Every unique series costs your time-series database memory and index space before anyone runs a query. Prometheus keeps recent samples for every active series in its in-memory head block at roughly three kilobytes per series, so 1 million active series consumes gigabytes of memory before a single dashboard loads. Query cost scales the same way, because the planner scans the index for matching series before touching sample data; in a historical production example, query latency increased from about 1.5 seconds at 100,000 series to about five seconds at 200,000 series for an instant query on one metric. Pricing models can translate that overhead into your invoice in different ways:
| Pricing model | Pricing unit | Cardinality exposure |
| Legacy custom-metric billing | Unique combinations of metric name, host, and tags | Multiplicative |
| Active-series billing | Active time series | Direct, per series |
| Gigabyte-ingested billing | Bytes ingested | Linear, indirect |
A label can ship unnoticed. An engineer adds a tag to improve debugging, the deploy goes green, and the series count can multiply quietly until the invoice arrives weeks later. Observability spend can rise faster than the infrastructure signals your team expects, which is why cardinality needs monitoring before it needs cutting.
Signs Your Observability Cardinality Is Out of Control
Cardinality problems announce themselves through performance and cost symptoms long before anyone inspects a series count. These signals deserve attention:
- Queries and dashboards slow down: Results that once returned in seconds take minutes, and dashboards can fail to load over long time ranges. The lag hits hardest mid-incident, when your on-call engineer needs answers fastest.
- Bills grow while traffic stays flat: Active series counts climb after deploys and autoscaling events even when request volume doesn’t move. Flat traffic with a rising cost curve points to a new label or Kubernetes churn.
- Backends hit hard limits: Prometheus develops memory pressure as active series grow, and series overhead can consume gigabytes of memory before queries run. Log and metrics backends may also reject new streams or series when traffic exceeds configured limits.
- Your engineers start cutting telemetry: Trace sampling drops, and label filters appear in collector configs to hold the invoice down. Teams rarely document this cost-driven data loss, so it resurfaces as missing context during incidents.
Any one of these signals justifies a cardinality audit before the next billing cycle, and together they mean cost pressure is already dictating your instrumentation decisions. A quick audit can show whether a new label or noisy exporter caused the jump.
Common (and Flawed) Approaches to Managing Cardinality
The instinctive responses to a cardinality spike all work in the short term, and each one removes context you’ll want back during the next incident, plus the operational debt of forgetting what got cut and why. Teams often reach for four shortcuts:
- Dropping labels or aggregating too early creates lasting gaps: Removing labels collapses their series irreversibly, and pre-aggregating too early can make later drilldowns impossible. The tenant or user dimension you delete to save money is often the one the next noisy-neighbor investigation needs.
- Aggressive sampling can hide rare errors: Head-based sampling cannot make decisions based on complete traces, so rare errors can disappear at the same rate as healthy requests. Tail-based sampling can keep every error trace, but it requires buffering complete traces before deciding, which gets resource-hungry at high volume.
- Manual allowlists and blocklists only hold cardinality down temporarily: The next deploy adds labels nobody reviewed, so counts may spike after every cleanup. A blocklist applied without auditing downstream queries silently corrupts the dashboards and alerts that rely on the dropped labels.
- Switching to cheaper cold storage shifts the cost, not the risk: Retrieval demand concentrates exactly where time pressure is highest: incident response and compliance audits. Slow rehydration turns a routine historical query into a delay you can’t afford mid-incident.
Each shortcut trades incident-time context for calm-time savings, and the bill for that trade arrives during the next outage. Controls that preserve queryability beat controls that delete data, so the goal is reducing waste without erasing the path to root cause.
How to Control Cardinality Without Losing Observability
You can keep business-critical labels and still hold cardinality in check by measuring first, then adding guardrails and tuning retention before you delete anything. Four practices make that possible:
- Get visibility before touching instrumentation: Use the series-count data Prometheus exposes through its TSDB status endpoint, and usage analysis can find stored metrics that teams never use. Ranking metrics and labels by series count and query frequency shows you what to change before you modify a single exporter.
- Set cardinality limits and alerts: An alert on active series growth (the prometheus_tsdb_head_series metric in self-hosted setups) catches a bad deploy in hours instead of a billing cycle. New labels should go through code review with an estimated cardinality attached, the same way reviewers handle schema changes.
- Use in-stream aggregation: Streaming aggregation keeps raw events queryable for investigations and stores lower-cardinality long-term series. Usage-driven aggregation can reduce long-term series counts without removing the raw context teams need for incident response.
- Choose cardinality-friendly pricing: Billing models that count tag combinations or active series turn every label into a budget consideration. Under ingestion-based pricing, labels are less likely to trigger per-tag or active-series surcharges, though metric volume and series growth still need governance.
How far these practices take you depends on the architecture underneath, because an indexing backend pays for every series whether or not anyone queries it, and that choice determines how much context you can afford to keep. Pricing then decides whether a useful label becomes a debugging asset or a budget risk.
More Techniques to Reduce Cardinality at the Source
The four practices above cover measurement and governance, but a few tactical moves can cut cardinality before data ever reaches your pipeline. These techniques work alongside the practices above:
- Drop unnecessary labels at the edge: Removing labels your team never queries, such as internal build hashes or debug flags, at the collector or agent level keeps them from ever becoming a stored series.
- Normalize labels: Standardizing label naming and casing across services, such as a single service_name convention instead of five variants, prevents the same dimension from fragmenting into duplicate series.
- Use logs and traces for high-variance data: Moving unbounded values like user IDs or request IDs out of metric labels and into logs or trace attributes keeps that detail queryable without inflating metric series counts.
None of these steps require ripping out existing instrumentation, since each one targets a specific source of avoidable series growth.
How Coralogix Controls High-Cardinality Costs for High-Cardinality Workloads
Coralogix reduces index-driven cardinality costs by analyzing data in-stream before storage. The Streama© architecture performs in-stream analysis on logs, metrics, traces, and security events as Coralogix ingests them, then writes the data to your own Amazon S3 bucket (or Google Cloud Storage on the US3 environment) in open Parquet format instead of a proprietary index. The TCO Optimizer routes each stream across Frequent Search, Monitoring, Compliance, and Blocked pipelines based on policies you define, which regularly saves customers 40 to 70 percent of observability costs. Pricing has no per-host or per-query charges, and metrics run at $0.05 per gigabyte (GB): a high-cardinality tenant label adds gigabytes as it adds series, but it isn’t metered again under a separate active-series or per-tag surcharge.
Cardinality governance is built into the platform. Metrics Usage Analyzer pinpoints expensive metrics based on query frequency and the cardinality/granularity profile, and the unique_series_daily metric tracks cardinality growth so you can alert on it before hitting the platform’s per-metric series limit. For traces, Coralogix adopts OTel cardinality limits with automatic, configurable control in the Span Metrics pipeline, and Events2Metrics generates long-term metrics from spans and logs so high-cardinality raw events never need indexing to feed a dashboard. Those controls let you keep raw investigative context while reducing the long-term series footprint your dashboards depend on.
Getting Cardinality Under Control Without Sacrificing Insight
Direct governance for high cardinality gives you better observability and lower bills. The labels that make debugging fast, such as tenant IDs and endpoints, are worth keeping when the underlying architecture reduces exposure to per-tag or active-series pricing premiums. Visibility through cost-control tools and limits that catch a bad deploy early turns cardinality into a routine engineering review instead of a billing surprise.
See what your own high-cardinality metrics would cost under Coralogix’s per-gigabyte pricing, which scales with the labels you actually keep rather than a separate active-series surcharge. Start a free 14-day trial to check the real numbers before your next billing cycle.
Frequently Asked Questions About High Cardinality
What is considered high cardinality in observability?
Prometheus guidance recommends avoiding labels with more than 10 possible values when possible, and investigating alternatives when a label can exceed 100 values. Total metric cardinality depends on the product of all label combinations, so an unbounded label such as a user ID or raw URL should be treated as high cardinality regardless of its current count.
Does high cardinality always mean higher costs?
It depends on the pricing model. Per-tag and per-series billing multiplies cost with every label combination, while ingestion-based per-GB models primarily charge for data volume. Self-hosted backends still pay in memory and index size as series counts grow, which makes governance worthwhile even when your vendor bills by volume.
Can you reduce cardinality without losing troubleshooting data?
Yes, use aggregation. Streaming aggregation keeps low-cardinality rollups for dashboards and long-term trends while raw, fully labeled data stays queryable for investigations. Moving unbounded dimensions like user IDs from metric labels into logs or traces preserves the diagnostic path and keeps metric series counts lower.
How is cardinality different from data volume?
Volume is the number of bytes you ingest, and it grows roughly linearly with traffic. Cardinality is the number of unique label combinations, and it grows multiplicatively with each new label, increasing memory and query time through larger indexes. A system can have modest volume and explosive cardinality at the same time.
What tools help monitor cardinality before it becomes a cost problem?
Prometheus exposes per-metric series counts through its TSDB status endpoint, and managed backends may ship cardinality dashboards. Coralogix tracks growth through the unique_series_daily metric and surfaces expensive metrics in Metrics Usage Analyzer, so you can alert on cardinality trends the same way you alert on error rates.