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):
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:
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 (sorequire_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=redisconfigured 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. Neitherssenorlogshas arequire_leadergate —"local"here means "single-replica broadcast only," not "replica blocked."
cache_backend¶
or, on error resolving the cache module:
GET /health/live response¶
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"]
}