Kerno ships as a single Docker image. The only thing it really cares about is DATABASE_URL pointing at a Postgres with the pgvector extension. Everything else (LLM key, auth, connectors, licence key) is configured through the wizard at /setup and stored encrypted in that database.
Three deployment shapes:
| Shape | DB | Best for |
|---|---|---|
| Bundled | Postgres + pgvector inside docker-compose.yml | Solo developers, evaluation, home labs. Default. |
| External | Customer-provisioned managed Postgres | Teams with existing infra. Want their own backups, VPC, snapshots. |
| Engine only | None | Read-only / degraded mode. Not viable for production — wizard can't save without a DB. |
Bundled Postgres
The canonical docker-compose.yml brings up the engine + a pgvector/pgvector:pg16 container together, with a healthcheck so the engine waits for the DB. Zero config — just docker compose up -d. See Quickstart.
services:
postgres:
image: pgvector/pgvector:pg16
# … bundled config …
app:
image: ghcr.io/getkerno/kerno:latest
environment:
DATABASE_URL: postgresql://kerno:${POSTGRES_PASSWORD:-kerno}@postgres:5432/kerno
depends_on:
postgres:
condition: service_healthy
External Postgres
Comment out the bundled postgres service. Point DATABASE_URL at a managed instance:
# .env
DATABASE_URL=postgresql://user:pass@host:5432/dbname
Two requirements on the database:
- Postgres 14 or newer
- The
pgvectorextension. One-click toggle on Supabase / Neon / Railway. Self-hosted:CREATE EXTENSION IF NOT EXISTS vector;
The engine creates the schema it needs on first boot:
app_config— encrypted config store (LLM key, OAuth, connector tokens, licence key)memory_chunks— pgvector semantic memorypgbossschema — persistent job queue- conversation/reactions tables
Step-by-step: External Postgres.
Why customers choose external Postgres
- Existing backup story — they already have nightly snapshots, point-in-time restore, replication. No reason to bolt a second backup process onto a single-host Docker volume.
- Compliance — DB has to live in a specific region/VPC, accessible only via private networking.
- Scaling — semantic memory grows over time. Managed Postgres scales storage independently of the engine.
- Audit trails — Postgres lives in their existing observability stack.
Trade-off: one more thing to provision. The bundled Postgres is genuinely fine for the first year of single-user use.
Engine only (no DB)
Skip DATABASE_URL entirely. Engine falls back to:
- in-memory job queue (lost on restart)
- no semantic memory (
memory_chunksnot created) - no encrypted config store — the wizard can't save anything
This isn't really a production option. It exists for the rare degraded-mode scenarios (CI smoke tests, ephemeral demo containers). Customers shouldn't pick this path.
Picking
| Need | Pick |
|---|---|
| Get running in five minutes | Bundled |
| Already pay for a managed Postgres | External |
| Multi-region or multi-instance | External (point both engines at the same DB) |
| Air-gapped on-prem | Bundled, mount the volumes to your own backup target |
You can switch later. Move the engine container to a different DATABASE_URL and run pg_dump / pg_restore between the two. The encryption key in memory/.encryption-key carries over so the encrypted config is readable in the new database.