Why it exists
Prometheus's own documentation is unambiguous that unbounded labels are a mistake, and unambiguous about the mechanism: every distinct combination of label values is a separate time series, each with its own memory, disk and query cost. That guidance is easy to read and easy to forget in the middle of instrumenting a service, because the mistake rarely looks dangerous locally. A single extra label argument in an instrumentation call looks like a small, reasonable addition. Its effect is not additive, it is multiplicative, and the difference between those two words is exactly the gap between a metric that stays healthy and one that takes a Prometheus instance down.
That gap is also not something a general-purpose chatbot can close by explaining the multiplication rule in prose, because the actual number depends entirely on inputs that only exist inside the asker's own system: how many methods their API has, how many distinct routes, how many replicas, how long they retain data, how often they scrape. A calculator that takes those specific numbers and returns a specific series count, samples-per-second rate and disk estimate answers a question that an explanation of the rule cannot.
What it does
- Model any number of metrics, each with its own labels, estimated per-label cardinality, and target/replica count.
- Compute active series as the product of each metric's label cardinalities, multiplied by its target count, summed across every metric.
- Compute samples per second and total samples from a scrape interval and retention period you set, in whatever units are convenient.
- Estimate disk as two separately-shown numbers — sample bytes and index bytes — rather than one blended figure, because they come from different sources and respond differently to churn.
- Flag a per-metric danger tier at two commonly-discussed thresholds, with the reasoning (memory, query latency) stated rather than left as an unexplained colour.
- Show every intermediate step, in a numbered receipt, so the final number is never presented without the arithmetic that produced it.
How the numbers are built, and checked
The active-series and samples-per-second numbers are closed-form arithmetic: multiplication for combining label cardinalities and target counts, division for converting a total into a rate. There is no approximation in that half of the model — if you can verify 5 × 6 × 20 = 600 by hand, you can verify the tool's series count by hand too.
The disk estimate is where real-world variation enters, and the tool is built to say so rather than hide it. Bytes-per-sample has a specific, cited source: Prometheus's own storage documentation states that Prometheus stores an average of roughly 1–2 bytes per sample for compressed chunks, and gives the exact formula this tool's pipeline mirrors — retention time in seconds, multiplied by ingested samples per second, multiplied by bytes per sample. That range is real but not universal, so the field is a number you can change, defaulted to a stated midpoint rather than hard-coded as fact. Index bytes per series has no equivalent published constant at all; the default here is explicitly labelled as an order-of-magnitude placeholder, not a specification value, because none exists to cite.
Before shipping, the underlying arithmetic (in cardinality.js, which has no DOM
dependency and can run standalone under Node) was cross-checked against a second, independently-written
implementation that recomputes the same real-world quantities by a deliberately different route: a
brute-force cartesian enumeration confirms that multiplying label cardinalities together (rather than
adding them, or taking their maximum) is the correct operation for small cases where every combination
can actually be listed out one at a time, and a separately-reordered version of the full pipeline
— computing total samples directly rather than via a stored samples-per-second, and accumulating
the disk total in the opposite order — was compared against thousands of randomised configurations.
Beyond that direct comparison, a set of invariants that any correct implementation of this model must
satisfy were checked explicitly: doubling the retention period doubles total samples without changing
active series; doubling one label's cardinality doubles that metric's series count; a metric with zero
labels produces exactly one series per target; doubling the churn multiplier doubles index bytes while
leaving sample bytes untouched. Every check passed with zero discrepancies before this page shipped.
The worked “what killed our Prometheus” example
The tool's own worked example — and the pair of buttons on the main page that let you trigger
it interactively — reconstructs a specific, common failure: an HTTP metric with a handful of
ordinary labels (method, status, route) sitting at a perfectly reasonable few thousand series, until a
user_id label is added to help debug one customer's issue. With roughly 50,000 distinct
users, that single label change multiplies the series count by 50,000, turning 6,000 series into 300
million. The scenario is deliberately built to be reproduced by anyone reading this, not asserted as an
anecdote: click “Load worked example”, then “Add a user_id label”, and watch the
series count update in real time.
What it deliberately does not do
- Connect to a real Prometheus instance. No URL field, no bearer token field, no
code path that could make that request even if one were added by mistake — there is no
fetch,XMLHttpRequestorWebSocketanywhere in either script file. - Model query cost precisely. It explains the two mechanisms (head-block memory, query-time series scanning) behind why cardinality matters, but does not attempt to simulate a specific PromQL query's latency, which depends on the query itself and not only on one metric's series count.
- Pretend the disk figure is exact. It is explicitly modelled as depending on two adjustable, honestly-sourced-or-labelled inputs, not presented as a single confident number the way a naive calculator might.
- Account for remote write, federation, or recording rules. Those change a real deployment's effective series and sample load in ways outside this tool's single-scrape-pipeline model.
Privacy and cost
Everything on this page runs as plain arithmetic in your browser tab. The metric names, label names and cardinality numbers you type are never transmitted, logged or stored — there is no endpoint on this page that could receive them and no code capable of using one. There is no account and nothing to install. Once the page has loaded, nothing further is fetched.
The site is free to use. It may carry advertising to cover hosting; see the privacy policy for what that would mean. An advertising script is third-party code that makes its own requests, but it cannot see the contents of a field it did not create, so “what you type never leaves your device” is a claim that survives it.
Who this is likely useful for
Platform and SRE teams reviewing a new service's instrumentation before it merges, where the question is not “does this compile” but “what does this do to the shared Prometheus instance once every replica ships it.” Teams sizing a Prometheus deployment for the first time, translating a list of planned metrics and an expected fleet size into a disk and memory conversation with whoever owns the infrastructure budget. And teams reconstructing a cardinality incident after the fact, who know a metric name and a suspect label but want the actual multiplication spelled out in a form they can put in a postmortem, rather than a rounded-off “it got big.”
It is less useful, by design, for anyone who already has a live Prometheus instance to query
directly — count by (__name__)({__name__=~".+"}) against the real thing will always
beat an estimate of it. This tool is for the moment before that instance exists, or before the metric
has shipped, when the only numbers available are the ones you can reason about in advance.
A note on terminology
“Series,” “sample,” and “target” get used precisely and
distinctly throughout this tool, because conflating them is a common source of confusion for anyone
newer to Prometheus. A series is one specific combination of a metric name and its
label values — it is an identity, not a data point. A sample is one measurement
of one series at one point in time — a single (timestamp, value) pair. A series that has existed
for a day at a 15-second scrape interval has contributed 5,760 samples, but it is still exactly one
series throughout. A target is one thing Prometheus scrapes — one instance, one
pod, one process exposing a /metrics endpoint — and is why this tool asks for a
target count per metric rather than assuming every metric comes from a single source.
Corrections
If a number here disagrees with a measurement from your own Prometheus instance in a way this page's stated limitations don't already explain, or a citation turns out to be stale, that is worth hearing about. The contact page has the address.