Skip to content

Health Endpoint Reference

Repod exposes three Kubernetes-compatible health endpoints (backend/routers/health_router.py). None require authentication.

Endpoint Purpose Status codes
GET /health Full health report — every probe, grouped by criticality. 200 (healthy/degraded), 503 (unhealthy)
GET /health/live Liveness probe — process is running. Intentionally minimal, no I/O or lock. 200 always
GET /health/ready Readiness probe — service can accept traffic. 200 (ready), 503 (a critical probe failed)

Status semantics

Status Meaning HTTP code on /health
healthy Every critical and non-critical probe passed. 200
degraded All critical probes passed; at least one non-critical probe failed. 200
unhealthy At least one critical probe failed. 503

GET /health response

{
  "status": "healthy | degraded | unhealthy",
  "timestamp": "ISO 8601 UTC",
  "version": "string (APP_VERSION env var, default \"dev\")",
  "checks": {
    "critical": { "...": "..." },
    "non_critical": { "...": "..." },
    "info": { "...": "..." }
  }
}

checks has three groups. Every probe result is an object with at least an "ok": bool field.


checks.critical

A failure in any of these four probes sets status = "unhealthy" and GET /health / GET /health/ready both return 503.

Field Probe Response shape
manifests /repos/manifests directory exists and is accessible. {ok, path, free_gb, total_gb, used_pct}
pool /repos/pool directory exists and is accessible. {ok, path, free_gb, total_gb, used_pct}
auth_db SELECT COUNT(*) FROM users succeeds against PostgreSQL. {ok, count} or {ok: false, error}
manifest_db SELECT COUNT(*) FROM manifests succeeds against PostgreSQL. {ok, count} or {ok: false, error}

GET /health/ready reports exactly these same four probes under checks, plus a top-level "ready": bool and, when not ready, a "failing": [...] array listing which probe names failed.


checks.non_critical

A failure here sets status = "degraded" (never unhealthy on its own) — /health still returns 200.

Field Probe Response shape
audit /repos/audit directory exists and is accessible. {ok, path, free_gb, total_gb, used_pct}
clamav clamscan --version runs successfully. {ok, version} or {ok: false, version: null, error}
reprepro reprepro --version binary is in PATH. {ok: true, version} or {ok: false, version: null, error}
gpg At least one private key exists in the GPG keyring (GNUPGHOME). {ok, fingerprint} or {ok: false, error}
scheduler APScheduler is running with active jobs. {ok, jobs: [...]} — see below
alembic alembic_version table is populated (not empty while application tables already exist). {ok, version} or {ok: false, version: null, error}

scheduler job entries

Each entry in jobs (when the scheduler is running):

{"id": "string", "name": "string", "next_run": "ISO 8601 or null", "paused": "bool"}

paused is true when next_run_time is null.

On a passive HA replica (see Environment variables and the HA sections of the architecture docs), no scheduler runs at all — this is expected and does not degrade the status:

{"ok": true, "jobs": [], "note": "réplique passive — scheduler sur l'instance leader"}

If the current instance is the leader and the scheduler failed to start, this becomes {"ok": false, "jobs": [], "error": "scheduler non démarré"}.


checks.info

Read-only informational data. Never affects status.

Field Purpose Response shape
packages Pool file counts and size, by format. see below
storage Filesystem usage on /repos plus per-directory size breakdown. see below
license Active license edition. {ok, edition, active, issued_to}
setup Whether the first-run setup wizard has completed. {ok: bool, setup_done: bool}
ha HA active-passive status and per-flow job-state backend. see below
cache_backend Active response-cache backend. {ok: true, backend: "memory" \| "redis"}

packages

{
  "ok": true,
  "total_manifests": 0,
  "pool_files": 0,
  "pool_size_mb": 0.0,
  "by_format": {"deb": 0, "rpm": 0, "apk": 0}
}

storage

{
  "ok": true,
  "free_gb": 0.0,
  "total_gb": 0.0,
  "used_pct": 0.0,
  "dirs": {
    "pool":      {"path": "/repos/pool", "size_mb": 0.0},
    "manifests": {"path": "/repos/manifests", "size_mb": 0.0},
    "audit":     {"path": "/repos/audit", "size_mb": 0.0},
    "grype_db":  {"path": "/repos/grype-db", "size_mb": 0.0},
    "clamav_db": {"path": "/var/lib/clamav", "size_mb": 0.0}
  }
}

size_mb is null when the directory doesn't exist.

ha

{
  "ok": true,
  "is_leader": "bool",
  "instance_id": "string",
  "scheduler_active": "bool",
  "job_state_backend": {
    "scan":    "redis | local",
    "install": "redis | local",
    "mirror":  "redis | local",
    "sync":    "redis | local",
    "sse":     "redis | local",
    "logs":    "redis | local"
  }
}
  • is_leader — whether this instance holds the PostgreSQL advisory lock (services/leader_election.py). Only the leader runs the APScheduler cron jobs.
  • job_state_backend.{scan,install,mirror,sync}"redis" means this flow's state is actually distributed across replicas right now (so require_leader_for(flow) becomes a no-op for it); "local" means the historical single-process behavior, either by default configuration or as a fail-soft fallback (JOB_STATE_BACKEND=redis configured but Redis was unreachable at startup) — both cases are indistinguishable from this field alone, by design.
  • job_state_backend.sse / .logs"redis" means live events (GET /dashboard/events) or log entries (GET /logs, GET /logs/stream) broadcast across every replica via Redis pub/sub; "local" means single-replica delivery only. Neither sse nor logs has a require_leader gate — "local" here means "single-replica broadcast only," not "replica blocked."

cache_backend

{"ok": true, "backend": "memory"}

or, on error resolving the cache module:

{"ok": true, "backend": "memory", "note": "error string"}

GET /health/live response

{"alive": true, "timestamp": "ISO 8601 UTC"}

Always 200. No I/O, no database access — purely confirms the FastAPI process is running.


GET /health/ready response

{
  "ready": "bool",
  "timestamp": "ISO 8601 UTC",
  "checks": {
    "manifests":   {"ok": "bool", "...": "..."},
    "pool":        {"ok": "bool", "...": "..."},
    "auth_db":     {"ok": "bool", "...": "..."},
    "manifest_db": {"ok": "bool", "...": "..."}
  },
  "failing": ["only present when ready is false — list of failed probe names"]
}