Deployment guide¶
How to run the Kneo Agent Dashboard in production. The dashboard is one deploy unit —
a FastAPI BFF that also serves the built SPA (browser UI) same-origin; there is no
separate frontend to host (ADR-008). It reaches the Kneo Agent Platform
through kneo-client over /v1.
For a ~10-minute first run see the Quickstart; for the full list of settings see the Environment-variable reference.
Two ways to run it¶
The container and the PyPI wheel are equivalent — both bundle the BFF and the built
SPA (ADR-011), so pip install gives the full dashboard without Docker.
docker run --rm -p 8090:8090 \
-e KNEO_URL="https://kneo-serv.internal" \
-e KNEO_API_KEY="…" \
-e KNEO_DASH_AUTH_MODE=oidc \
-e KNEO_DASH_SESSION_SECRET="$(openssl rand -base64 32)" \
-e KNEO_DASH_OIDC_ISSUER="https://idp.example/realms/kneo" \
-e KNEO_DASH_OIDC_CLIENT_ID=kneo-dash \
-e KNEO_DASH_OIDC_CLIENT_SECRET="…" \
-e KNEO_DASH_OIDC_REDIRECT_URL="https://dash.example/api/callback" \
-e KNEO_DASH_OIDC_ROLE_MAP='{"kneo-admins":"admin","kneo-ops":"operator"}' \
-v kneo-dash-state:/var/lib/kneo-dash \
ghcr.io/kneo-agent/kneo-dash:latest
The image serves on :8090 as a non-root user (uid 10001), bundles the SPA at
/app/static (KNEO_DASH_SPA_DIR is preset), and defaults the state store to a SQLite
file under /var/lib/kneo-dash — mount a volume there so it survives restarts (or
point KNEO_DASH_DB_URL at Postgres). Pin by digest in production.
pip install kneo-dash # or 'kneo-dash[postgres]' for an external DB
export KNEO_URL="https://kneo-serv.internal" KNEO_API_KEY="…"
export KNEO_DASH_AUTH_MODE=oidc KNEO_DASH_SESSION_SECRET="$(openssl rand -base64 32)"
export KNEO_DASH_OIDC_ISSUER="https://idp.example/realms/kneo" \
KNEO_DASH_OIDC_CLIENT_ID=kneo-dash KNEO_DASH_OIDC_CLIENT_SECRET="…" \
KNEO_DASH_OIDC_REDIRECT_URL="https://dash.example/api/callback" \
KNEO_DASH_OIDC_ROLE_MAP='{"kneo-admins":"admin","kneo-ops":"operator"}'
kneo-dash # serves UI + API on http://127.0.0.1:8090
The published wheel bundles the browser UI — the BFF serves it same-origin, so
pip install is the full dashboard, identical to the container. (A pip install -e .
dev checkout that hasn't built the SPA runs API-only — /api/* with no UI.)
The kneo-dash console script runs the server (serve, the default). It also exposes the
out-of-band kneo-dash recover break-glass path used after a restore — see the backup &
recovery guide.
Compose (with a TLS-terminating proxy)¶
examples/docker-compose.prod.yml
runs the dashboard behind an nginx reverse proxy (examples/nginx.conf)
that terminates TLS and forwards to :8090. Copy examples/.env.example, fill in the
platform + OIDC values, and docker compose -f examples/docker-compose.prod.yml up -d. The
OIDC redirect URL must be the public https://…/api/callback the browser reaches.
State store: SQLite vs Postgres¶
| SQLite (default) | Postgres | |
|---|---|---|
| When | single replica; the common case | multi-replica / HA |
| Config | unset KNEO_DASH_DB_URL (container → /var/lib/kneo-dash/state.db on a volume) |
KNEO_DASH_DB_URL=postgresql://… + pip install 'kneo-dash[postgres]' |
| Support | supported | best-effort |
Schema migrations run automatically on startup (forward-only); a database whose schema is newer than the running image fails fast rather than risk a partial downgrade. Keep the image and the database in step across upgrades.
State-store growth & disk sizing¶
The store is append-heavy — launch history and the audit log grow over time (sessions
are purged automatically; see KNEO_DASH_SESSION_PURGE_INTERVAL_SECONDS). Size the state
volume for steady growth and monitor it:
kneo_dash_store_state_bytes(on the/metricssurface) reports the complete SQLite footprint — the main DB plus its-wal/-shmsidecars. Alert on sustained growth, not an absolute number. (The series is absent on Postgres — size the database with your normal PG tooling there.)kneo_dash_store_rows{table="audit_log"|"launches"}andkneo_dash_sessions_active(0.8.0) report the row counts behind that footprint — watch these to decide when to prune.- Retention/pruning ships (0.8.0): the operator-safe
kneo-dash pruneCLI bounds launch-history/audit growth — export-before-delete (versioned + checksummed) with a self-audit record. Size the volume for steady-state between prunes, and prune on the row-count signal above.
Health probes¶
Three endpoints, with HTTP codes chosen so a degraded dependency is never mistaken for a dead process — the split exists specifically to avoid a restart-loop footgun:
| Endpoint | Use as | Behaviour |
|---|---|---|
GET /api/livez |
liveness probe | 200 iff the process is up — no dependency checks. Never fails on a store/OIDC hiccup, so an orchestrator won't restart a healthy process. |
GET /api/readyz |
readiness / traffic gate | 200 when the state store answers and the instance isn't in recovery mode; 503 otherwise (the proxy de-routes it). |
GET /api/healthz |
dashboards / humans | always 200 + a JSON body reporting liveness, readiness, and dependency status (incl. OIDC). A degraded dependency shows in the body, never as a 503 — so it can't be misused as a liveness probe. |
Point the orchestrator's liveness probe at /api/livez and its readiness probe at
/api/readyz. /api/healthz is for humans and monitoring, not probes.
Post-deploy verification¶
"Did it come up correctly?" The Dashboard ships no operator smoke script — this is a manual curl + UI checklist. Run it after every deploy/upgrade.
1. Probes (curl). From a shell that can reach the BFF:
curl -fsS https://dash.example.com/api/livez # → 200 {"status":"alive"} — the process is up
curl -fsS https://dash.example.com/api/readyz # → 200 when the store answers + NOT in recovery
readyz503= the state store is unreachable or the instance is in recovery (a detected restore). CheckGET /api/healthz(always 200) for the JSON breakdown; if it's recovery, runkneo-dash recover --statusand reconcile before serving.
2. UI golden path (logged in). Sign in (OIDC) and walk one run end-to-end — each step with its expected result:
- [ ] Login → you land on the Overview with your role's nav (a wrong/empty role = an OIDC role-map problem — see security hardening).
- [ ] Launch a spec — Load → Deploy → Run — Deploy goes green, Run enables, and you route to the new run's detail.
- [ ] Trace tail — open the run's Trace tab; live events stream (SSE) as it executes.
- [ ] Audit read — open Audit (needs
audit.read) and confirm the run/launch shows in the platform timeline.
If all four pass, the platform connection, auth, capability enforcement, SSE, and audit path are all live. This mirrors the end-to-end operator flow (a scripted version is a future tutorial); keep it in your runbook.
Operational notes¶
- Run as non-root. The image already does (uid
10001); keep it that way behind your orchestrator's security context. - Authentication. Use
KNEO_DASH_AUTH_MODE=oidcfor any shared/production deploy — the defaultstaticmode is unauthenticated and refuses to start withoutKNEO_DASH_DEV_MODE=1. See the security-hardening checklist. - Same-origin only. The shipped image serves the SPA same-origin; credentialed
cross-origin hosting is out of scope (ADR-009). Leave
KNEO_DASH_CORS_ORIGINSempty.
See also¶
- Environment-variable reference — every setting, grouped by concern.
- Connecting — the
kneo-clientprofile, environments, the state store. - Quickstart — the ~10-minute first run.