Skip to content

Gauge vs counter: Prometheus metric types explained

Al Brown
Last updated: Jul 29, 2026

A counter is a Prometheus metric whose value only increases, or resets to zero on restart, and is meaningful through its rate of change. A gauge is a metric whose value moves up and down and is meaningful as read. They are two of the four core metric types Prometheus defines, alongside histograms and summaries.

What is a counter metric?

A counter is a cumulative metric representing a single monotonically increasing value. The running total climbs for as long as the process lives and starts again from zero when it restarts. Raw counter values are rarely useful on their own; what matters is the rate of change, which turns the running total into events per second.

Counters count things that happen: requests served, errors returned, bytes transmitted, jobs completed. Canonical examples are http_requests_total and node_network_receive_bytes_total; the OpenMetrics specification standardises the _total suffix for counter sample names. The Prometheus metric types documentation is explicit about the boundary: "Do not use a counter to expose a value that can decrease." A count of currently running processes belongs in a gauge, because processes finish.

What is a gauge metric?

A gauge is a metric that represents a single numerical value that can arbitrarily go up and down. Unlike a counter, a gauge is meaningful as read: memory in use, queue depth, active connections, temperature. A gauge answers "what is the level right now?" rather than "how often is this happening?"

Typical gauges include process_resident_memory_bytes, node_memory_MemAvailable_bytes, and in-flight request counts. Client libraries let a gauge be set, incremented, or decremented, whereas a counter only increments. The query semantics differ too: gauges aggregate directly, so avg(), max(), and sum() across instances produce meaningful values, and delta() or deriv() describe how a gauge changed over a window.

What is the difference between a gauge and a counter?

The difference is monotonicity. A counter only increases and measures how often something happens; its absolute value depends on when the process last restarted, so only its rate of change carries meaning. A gauge moves in both directions and measures the current state of something; its absolute value is the measurement.

That one property determines which client-library operations exist (counters have no decrement), which PromQL functions apply (rate() for counters, delta() for gauges), and what a restart does to the data (a counter falls to zero, a gauge simply reports the new true level).

What metric types does Prometheus support?

Prometheus supports four core metric types: counters, gauges, histograms, and summaries. Histograms count observations into configurable buckets; summaries compute quantiles on the client over a sliding window. Prometheus v2.40 (November 2022) added native histograms, which use exponential bucketing and need no manual bucket configuration.

TypeCan it decrease?How you read itOpenTelemetry equivalentExample
CounterNo (resets to zero on restart)rate(), increase()Counter (monotonic Sum)http_requests_total
GaugeYesDirectly: avg(), max(), delta()Gauge or UpDownCounterprocess_resident_memory_bytes
HistogramNo (bucket counts only grow)histogram_quantile() over bucketsHistogramhttp_request_duration_seconds
SummaryQuantile values move; count and sum only growPrecomputed quantiles, read directlyNone (legacy compatibility only)rpc_duration_seconds

The OpenTelemetry column follows the Prometheus and OpenMetrics compatibility spec: an OTel Counter exports as a Prometheus counter, an OTel Gauge as a gauge, and an UpDownCounter (a sum that can decrease, such as items in a queue) also lands as a Prometheus gauge, because Prometheus has no non-monotonic counter. For latency data the mapping matters twice over: the histogram's bucket layout determines percentile accuracy, and percentiles rather than averages are the standard read on that data because latency distributions are right-skewed.

Why do counters reset and how do I handle it?

Counters reset because they live in process memory: when the process restarts, the running total starts again from zero. PromQL's rate() and increase() absorb resets automatically: any decrease in a counter's raw value is interpreted as a reset, and the calculation adjusts for the break in monotonicity.

The PromQL function documentation states it directly for both functions: "Breaks in monotonicity (such as counter resets due to target restarts) are automatically adjusted for." Two practical rules follow from that mechanism:

  • Always rate() before you aggregate. sum(rate(...)) sees each series' resets individually; rate(sum(...)) sums away the drops that mark a reset and miscounts.
  • Never rate() a gauge. Legitimate decreases look like resets to the heuristic; use delta() or deriv() for gauges instead.
# Correct: derive the per-series rate, then aggregate
sum by (service) (rate(http_requests_total[5m]))

When should I use a gauge vs a counter?

Use a counter when you care about how often something happens and the value never legitimately goes down: requests, errors, bytes, completed jobs. Use a gauge when you care about the current level of something that rises and falls: memory usage, queue depth, active sessions, disk space remaining.

Two questions settle almost every case:

  • Can the value go down in normal operation? If yes, it is a gauge.
  • Is the rate what you actually chart or alert on? If yes, it is a counter.

Some quantities are worth instrumenting both ways: queue_length as a gauge tells you the backlog now, while items_enqueued_total and items_dequeued_total as counters tell you throughput on each side. The distinction maps onto alerting practice: of the four golden signals, traffic and errors are counter-shaped, saturation is gauge-shaped, and latency belongs in histograms.

Working with counters and gauges in ClickStack

ClickStack, the ClickHouse-based observability stack, ingests metrics through the OpenTelemetry Collector, so the type semantics above carry through unchanged: sums keep their IsMonotonic and aggregation-temporality metadata, and gauges arrive as point-in-time values. Each data point is stored as a raw row in ClickHouse rather than a pre-downsampled series, so rates and levels can be re-derived over any window at query time. Against a simplified version of the schema, a per-service rollup looks like this:

SELECT
    toStartOfMinute(TimeUnix) AS minute,
    ResourceAttributes['service.name'] AS service,
    max(Value) AS cumulative_requests
FROM otel_metrics_sum
WHERE MetricName = 'http.server.request.count'
GROUP BY minute, service
ORDER BY minute

HyperDX, ClickStack's UI, charts these metrics next to the logs and traces from the same services, so a counter spike links directly to the requests behind it. ClickStack is open source; the getting started guide covers the metrics schema these queries run against.

FAQ

What is a histogram metric?

A histogram counts observations, such as request durations, into configurable buckets, exposing a _bucket series per bound plus _sum and _count totals. Quantiles like p99 are computed at query time with histogram_quantile(). Because bucket counts are themselves counters, histograms aggregate correctly across instances and time windows.

Can a counter ever go down?

A counter's raw value decreases only when the process restarts and the count begins again from zero. PromQL treats any decrease as a reset, never as real data. If a value genuinely falls during normal operation, model it as a gauge, or as an UpDownCounter in OpenTelemetry.

Is a Prometheus summary the same as a histogram?

No. A summary computes quantiles on the client over a sliding window and streams the finished values, which cannot be aggregated across instances; averaging per-host p99s is statistically invalid. A histogram ships raw bucket counts instead, so quantiles are computed at query time over any set of series.

Can I use rate() on a gauge?

No. rate() assumes monotonic input and interprets every decrease as a counter reset, so a normally fluctuating gauge produces spurious spikes. Use delta() for the change in a gauge over a window, or deriv() for its per-second trend based on linear regression.


Share this resource

  • Y Combinator icon
  • X icon
  • Bluesky icon
  • Facebook icon
  • LinkedIn icon

Subscribe to our newsletter

Stay informed on feature releases, product roadmap, support, and cloud offerings!

More like this

What is synthetic monitoring?

Al Brown • Last updated: Jul 29, 2026

Synthetic monitoring runs scripted probes (HTTP checks, API transactions, browser journeys) on a schedule from controlled locations to catch outages before users do.

Continue reading ->

What is SRE? Site reliability engineering explained

Al Brown • Last updated: Jul 29, 2026

Site reliability engineering (SRE) applies software engineering to operations problems, using SLOs, error budgets, a 50% toil cap, and blameless postmortems to keep systems reliable.

Continue reading ->

What is shadow AI? The governance gap in AI adoption

Al Brown • Last updated: Jul 29, 2026

Shadow AI is the use of AI tools and models inside an organization without IT or governance approval. It drives data leakage, unbudgeted spend, and compliance exposure, and detecting it is an observability problem.

Continue reading ->