StatView is a Flask + HTMX frontend for Prometheus metrics.
If you have a bunch of time-series statistics in Prometheus and want an easy way to browse and visualise them, without the hassle of pre-creating dashboards, this is the app for you.
It supports:
- Metric browsing.
- Click-to-graph behavior.
- Window/step selection.
- Live updates (periodic refresh).
- Comparison between two selected timeframes.
- A six-panel standard timeframe view.
- Dashboard creation
I wanted to browse through my metrics, see the latest info, see a comparison of other time periods, and easily send the URL of a specific metric to colleagues.
I don't want to create dashboards in advance based on guessing what I might want to see. And Grafana makes it really hard to even quickly change a graph from one stat to another.
I also loved StatHat, RIP.
StatView ships two operating modes. SECRET_KEY is required in both modes — it signs the Flask session cookie. Generate one with:
python -c 'import secrets; print(secrets.token_urlsafe(48))'If AUTH_TYPE is unset or none, StatView has no built-in authentication. Anyone who can reach the listening port can read every metric, create/rename/delete saved views, and modify dashboards. Deploy it only behind a trusted boundary — a reverse proxy with auth (oauth2-proxy, Cloudflare Access, Tailscale, basic-auth via nginx, etc.), a VPN, or a private network segment.
Set AUTH_TYPE=github and the variables below. StatView will gate every URL (except /healthz) behind a GitHub OAuth sign-in. Saved views and dashboards remain shared across all authorized users.
| Variable | Required | Description |
|---|---|---|
AUTH_TYPE |
yes | Set to github |
GITHUB_CLIENT_ID |
yes | From your GitHub OAuth app |
GITHUB_CLIENT_SECRET |
yes | From your GitHub OAuth app |
GITHUB_ALLOWED_USERS |
one of these two | Comma-separated GitHub logins, e.g. alice,bob |
GITHUB_ALLOWED_ORG |
one of these two | GitHub org slug; members of the org are allowed in |
OAUTH_REDIRECT_URL |
no | Override callback URL when behind a reverse proxy whose external host StatView can't autodetect. Default: {scheme}://{host}/auth/callback. |
Create the OAuth app at GitHub → Settings → Developer settings → OAuth Apps → New OAuth App. Set the Authorization callback URL to https://your-statview-host/auth/callback.
Notes:
- Allowlist changes take effect on the next request — no restart needed. Org membership is cached for 60 minutes.
- If
AUTH_TYPE=githubis set but any required variable is missing, every URL (including/healthz) returns 500 with an error message describing what's missing. The container stays up so you see the problem in the browser instead ofdocker logs. - HTTPS behind a reverse proxy: set
SESSION_COOKIE_SECURE=trueso the session cookie is only sent over HTTPS. Flask cannot detect the external scheme on its own when the proxy terminates TLS, so without this the cookie'sSecureflag won't be set even though clients reach you over HTTPS.
The image is published to GitHub Container Registry:
docker pull ghcr.io/richard5mith/statview:latestQuick start with docker run:
docker run --rm \
-p 8000:8000 \
-e PROMETHEUS_URL=http://your-prometheus:9090 \
-e SECRET_KEY=$(python -c 'import secrets; print(secrets.token_urlsafe(48))') \
-v statview-data:/app/data \
ghcr.io/richard5mith/statview:latestOr with docker compose — see docker-compose.ghcr.yml for a published-image example:
cp .env.example .env # set PROMETHEUS_URL
docker compose -f docker-compose.ghcr.yml up -dAvailable tags: latest (most recent push to main), v<semver> for tagged releases, sha-<short> for any specific commit.
StatView is configured via environment variables:
| Variable | Default | Description |
|---|---|---|
PROMETHEUS_URL |
http://localhost:9090 |
URL of the Prometheus server. |
PROMETHEUS_USERNAME |
(unset) | Optional HTTP basic-auth username for Prometheus. |
PROMETHEUS_PASSWORD |
(empty) | Optional HTTP basic-auth password. Only used when PROMETHEUS_USERNAME is set. |
LIVE_REFRESH_SECONDS |
15 |
How often the UI polls for new data. |
The button above deploys StatView from this repo using the Docker image built and stored here on GitHub. You will need to point it at your own Prometheus.
Prometheus stores time-series samples keyed by metric name and labels. This app uses:
/api/v1/label/__name__/valuesto list all metric names./api/v1/query_rangeto fetch graph data for chosen time windows.
- Python 3.12
- Flask
- HTMX
- Chart.js
uvfor dependency and run workflowsrufffor lintingpytest+pytest-covwith--cov-fail-under=85- Docker (single
Dockerfile, compose overrides for dev/prod runtime mode)
Local dev runs in Docker — ./app, ./alembic, and ./data are bind-mounted into the container, so editing files on the host triggers Flask's auto-reload inside the container without a rebuild.
First time:
cp .env.example .env # set PROMETHEUS_URL
tools/run-dev.sh # docker compose up --build -d, picks up the dev overrideAfter the first build, plain docker compose up (no --build) is enough. Re-run tools/run-dev.sh whenever you change pyproject.toml/uv.lock or anything else baked into the image.
Open http://localhost:8000 once it's up. Tail logs with docker compose logs -f statview.
Linux users: the container runs as UID 1000 by default. If your host user has a different UID, set
STATVIEW_UIDandSTATVIEW_GIDin.env(typicallySTATVIEW_UID=$(id -u),STATVIEW_GID=$(id -g)) so the bind-mounted./datadirectory is writable from inside the container.
Use the wrappers in tools/:
tools/check.sh # ruff check + ruff format --check
tools/test.sh # pytest with the 85% coverage gate
tools/db.sh # open the sqlite store in sqlite3 (auto-migrates if missing)These run via uv on the host — you'll need uv installed (brew install uv or curl -LsSf https://astral.sh/uv/install.sh | sh). They do not require the dev container to be running.
If you want to test what the published image actually does (no bind-mounts, gunicorn instead of flask --debug):
docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -dIf you specifically want to skip Docker — for instance to attach a debugger that does not like containers — you can run the app the way the entrypoint does:
uv sync --group dev
export PROMETHEUS_URL=http://your-prometheus:9090
uv run alembic upgrade head
uv run flask --app app.main:create_app run --host 0.0.0.0 --port 8000 --debugNote that the deployment target is always Docker, so this path is purely for ergonomic exceptions.
MIT.
