K
KernoDocs
Docs/Deployment options

Deployment options

Three ways to run Kerno — bundled Postgres, external Postgres, or hand-managed. Pick what fits your infra.

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:

ShapeDBBest for
BundledPostgres + pgvector inside docker-compose.ymlSolo developers, evaluation, home labs. Default.
ExternalCustomer-provisioned managed PostgresTeams with existing infra. Want their own backups, VPC, snapshots.
Engine onlyNoneRead-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 pgvector extension. 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 memory
  • pgboss schema — 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_chunks not 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

NeedPick
Get running in five minutesBundled
Already pay for a managed PostgresExternal
Multi-region or multi-instanceExternal (point both engines at the same DB)
Air-gapped on-premBundled, 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.