Skip to content
VaultTerm
Browse docs

self-hosting

Self-hosting overview

Run VaultTerm yourself as a single Docker Compose unit — the modular monolith plus Postgres and Redis, env-driven, no Kubernetes.

Updated Jun 23, 2026

VaultTerm self-hosts as a single modular-monolith unit orchestrated with Docker Compose. There is one application process — not a fleet of services — so the supported deployment unit is Compose, not Helm or Kubernetes. The same image you run in dev runs in staging and on-prem; only .env changes.

What runs

The stack is three containers defined in deploy/onprem/docker-compose.yml:

ServiceImagePurpose
appvaultterm/app (built from this repo)API + web portal on :4000; admin/platform plane on :4100
dbpostgres:16tenant data, isolated per organization
cacheredis:7sessions, rate limits, step-up proofs

The app container serves the web portal same-origin with the API on :4000, and hosts the admin/platform plane on a second port, :4100. There is no separate frontend service, no API gateway, and no orchestrator to operate — one container is the whole application tier.

Env-driven, one image everywhere

Every difference between environments lives in .env, read automatically by docker compose. The image is built once and carries no environment-specific configuration. Pointing the same image at a different database, public URL, key provider, or AI backend is purely a matter of editing .env and recreating the container — there is no rebuild for a config change. This is what makes the dev, staging, and on-prem deployments the same artifact. See the Configuration reference for the full set of variables.

Migrations run on boot

The app container applies database migrations on every boot. They are tracked in schema_migrations and idempotent, so docker compose up after an image upgrade is a complete deploy — there is no separate migration step to run by hand. Migrations are forward-only; take a backup before upgrading (see Backups and recovery).

TLS and the admin plane

The bundle serves plain HTTP on :4000. Terminate TLS in a reverse proxy in front of it — WebAuthn and secure cookies require HTTPS, and localhost is the only exempt origin. Proxy :4000 for the tenant app.

The admin/platform plane on :4100 binds to loopback by default (ADMIN_BIND=127.0.0.1). Reach it over an SSH tunnel or a trusted private interface; never expose it on the public internet, and never proxy it publicly.

Where to go next