promforecast is an open-source forecasting service for Prometheus-compatible metrics. It reads a YAML config, queries a long-term TSDB (VictoriaMetrics by default), runs forecasting models, and exposes the predictions on /metrics — so a forecast becomes a first-class metric: graphable in Grafana, alertable through Alertmanager, indistinguishable from anything else in your monitoring stack.

The idea is simple: you already know what your metrics did. promforecast tells you what they’re about to do, in the same language your stack already speaks.

What it is

A small Python service that runs as a normal Kubernetes Deployment next to your existing Prometheus. It never touches production Prometheus — all reads go through the long-term TSDB (VictoriaMetrics, Mimir, Thanos all work). An internal scheduler refits the models on an interval; between refits, /metrics serves the most recent forecast snapshot.

It’s stateless. There’s no database to operate. Models are refit on a schedule and the results live in the scraped /metrics endpoint, so a restart just means the next run reproduces everything.

It’s config-driven. Adding a metric to forecast is a YAML change, not a code change — point a PromQL query at the thing you care about and the forecast shows up as a metric.

What it can do

  • Forecast any PromQL query. Whatever you can express in PromQL — CPU saturation, disk fill, request rate, queue lag, business KPIs — can be forecast. PromQL in, Prometheus exposition format out.
  • Multiple models, automatically chosen. Ships with AutoARIMA, AutoETS, SeasonalNaive, MSTL and Theta (from Nixtla’s statsforecast). You can emit every model side by side, or let it cross-validate and pick the lowest-error model per series — and fall back to a safe baseline when even the winner fits the data poorly.
  • Confidence bands. Forecasts come with upper/lower bounds at the confidence levels you choose (e.g. 80% and 95%).
  • Free anomaly signal. It can emit how far the current actual value sits from the forecast band. A good forecast plus a large deviation is a baseline-aware anomaly alert — no separate anomaly system required.
  • A quality score per forecast. Each series gets a 0.0–1.0 reliability score combining backtest error, band width and data coverage, so you can hide or gate the forecasts you shouldn’t trust.
  • Accuracy you can alert on. Backtest error (MAPE/MASE) is emitted as metrics across multiple horizons, so “this model has degraded” is just another alert.

Built to behave in a real monitoring stack

  • Forecasts are just metrics. Same labels, same retention path, same dashboards. You compose with the recording rules and alerts you already know, rather than learning a new system.
  • Cardinality guardrails. Hard limits on series-per-query and total series, with configurable overflow behaviour (drop lowest priority, reject, or sample) — forecasting is a cardinality multiplier and the safety controls are first-class.
  • Doesn’t reinvent Prometheus. It emits raw forecasts; alerting goes to Alertmanager, visualisation to Grafana, derived metrics to recording rules.
  • Drop-in install. A Helm chart for the forecaster, and an umbrella chart that bundles VictoriaMetrics, dashboards and example alerts for a one-command start.

A few examples

Install it next to an existing Prometheus with Helm:

helm repo add promforecast https://esops-dev.github.io/promforecast
helm install promforecast promforecast/promforecast-stack

Forecasting a metric is a few lines of YAML. Point a query at node CPU and ask for a 24-hour forecast:

datasource:
  url: http://victoriametrics:8428/

defaults:
  lookback: 14d
  step: 5m
  horizon: 24h
  models: [AutoARIMA, SeasonalNaive]
  confidence_levels: [80, 95]

groups:
  - name: node_capacity
    queries:
      - id: node_cpu_busy_pct
        promql: |
          100 - (avg by (instance)
            (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

That id becomes the metric name. The forecaster now exposes, on /metrics:

node_cpu_busy_pct_forecast{instance="web-1", group="node_capacity", model="AutoARIMA", horizon="24h"}        72.4
node_cpu_busy_pct_forecast_lower{instance="web-1", group="node_capacity", model="AutoARIMA", level="95"}     61.0
node_cpu_busy_pct_forecast_upper{instance="web-1", group="node_capacity", model="AutoARIMA", level="95"}     83.8

Because it’s just a metric, “will this disk fill within a day?” is an ordinary PromQL alert against the forecast:

- alert: ForecastDiskFillsSoon
  expr: node_filesystem_avail_bytes_forecast{horizon="24h"} < 5368709120  # 5 GiB
  for: 15m
  labels:
    severity: warning

Let the forecaster pick the best model per series and fall back to a safe baseline when the fit is poor:

defaults:
  models: [AutoARIMA, AutoETS, SeasonalNaive]
  accuracy:
    evaluate: true
    auto_select: true                 # cross-validate, emit only the winner
    fallback_model: SeasonalNaive     # used when the winner still fits badly
    fallback_mape_threshold: 50       # percent

Turn on the deviation and quality signals, then alert only on deviations from forecasts you actually trust:

defaults:
  emission:
    deviation: true
    quality: true
- alert: ForecastDeviation
  # actual is outside its predicted band, and the forecast is reliable enough
  expr: |
    forecast_deviation_outside_band == 1
      and on (id, group) forecast_quality_score > 0.6
  for: 15m

Operational metrics

Beyond the forecasts themselves, it exposes the metrics you’d want to run it in production — last run timestamp, run duration, series counts, drops, failures, fit duration, and the accuracy/quality scores — so the forecaster monitors itself the same way everything else in your stack does:

forecast_accuracy_mape{id, group, model, horizon}
forecast_quality_score{id, group, model}
forecast_last_run_timestamp_seconds{group}
forecast_series_dropped_total{group, reason}
forecast_failures_total{group, reason}

In short

If you run Prometheus and keep eyeballing graphs to guess when something will saturate, promforecast turns that guess into a metric. Write a PromQL query, get a forecast with confidence bands, a quality score and a deviation signal — all in plain Prometheus exposition format, ready for the dashboards and alerts you already have.