Skip to content

Metrics (Prometheus)

The backend serves request, background-job and database-pool metrics in the Prometheus text format at /metrics. The endpoint is off by default.


Set METRICS_TOKEN in backend/container/.env (see Environment Variables), then restart the backend:

Terminal window
python -c "import secrets; print(secrets.token_urlsafe(48))"
  • The token must be at least 32 characters.
  • With METRICS_TOKEN empty, /metrics answers 404.
  • A missing or wrong token gets 401.
  • The token is a secret and is never logged. Keep the file that holds it readable by Prometheus only.

Prometheus must reach the backend container directly on the container network, for example backend:8000 with Compose. The frontend’s nginx does not proxy /metrics, so the public URL serves the web app instead. Do not publish the backend port to get around this.

scrape_configs:
- job_name: patrimoine
metrics_path: /metrics
authorization:
type: Bearer
credentials_file: /etc/prometheus/patrimoine-metrics-token
static_configs:
- targets: ["backend:8000"]

The older bearer_token_file key works too. To check the endpoint by hand from a container on the same network:

Terminal window
curl -H "Authorization: Bearer $METRICS_TOKEN" http://backend:8000/metrics

Name Type Labels Meaning
patrimoine_http_requests_total counter method, route, status_class HTTP requests handled by the backend
patrimoine_http_request_duration_seconds histogram method, route, status_class Request duration in seconds; buckets 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10 and 30
patrimoine_job_runs_total counter job, outcome Background job runs; outcome is success or error
patrimoine_db_pool_size gauge none Configured size of the database connection pool (PostgreSQL only)
patrimoine_db_pool_checked_out gauge none Connections currently in use (PostgreSQL only)
patrimoine_db_pool_overflow gauge none Connections open beyond the pool size; stays 0 until the pool is exhausted (PostgreSQL only)
patrimoine_build_info gauge version Always 1; the label carries the backend version

  • route is the route template, for example /api/v1/accounts/{account_id}, never the raw path. Paths that match no route are unmatched.
  • method is one of GET, POST, PUT, PATCH, DELETE, HEAD or OPTIONS. Any other method is OTHER.
  • status_class is 1xx to 5xx.
  • Scrapes of /metrics itself are not counted.
  • job is the name of the background job: daily-backup, recurring-snapshots, planned-transaction-sweep, alert-check, fx-refresh, audit-cleanup, session-purge, revoked-token-purge, webhook-delivery-cleanup, delta-cache-refresh, price-refresh, scheduled-reports and webhook-delivery-retry. See Background Worker for what each one does.

Without REDIS_URL, the jobs run in the backend process and are counted there.

With the arq worker, each run is counted in the Redis hash patrimoine:metrics:job_runs, and the backend reads that hash on every scrape. This needs the redis package in the backend image. The default image, published or built locally, includes it. Only a custom UV_EXTRA_ARGS that omits --extra redis removes it; podman.sh build adds it whenever REDIS_URL is set. When Redis cannot be read, the worker series are left out and the backend logs one warning.

Limits of the counts:

  • success means the job ran to its end. Per-user failures in the FX or delta cache refresh are logged and the run still counts as a success.
  • A worker daily backup with AUTO_BACKUP off counts as a success.
  • A worker job stopped by arq’s own timeout is not counted.

The three patrimoine_db_pool_* gauges exist only on PostgreSQL. On SQLite they are absent.


  • The backend’s own counters restart from zero when it restarts. The worker’s counters restart when Redis loses its data; the hash has no expiry. Prometheus rate() and increase() handle these resets.
  • The backend runs as a single process. Running several uvicorn workers would give per-process counters, which is not supported.