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:
| Service | Image | Purpose |
|---|---|---|
app | vaultterm/app (built from this repo) | API + web portal on :4000; admin/platform plane on :4100 |
db | postgres:16 | tenant data, isolated per organization |
cache | redis:7 | sessions, 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
- Install with Docker Compose — the connected install procedure.
- Air-gapped install — build a bundle and install with no network.
- Licensing and activation — the offline
.vtliclicense. - Backups and recovery — what to back up and how to recover.
- Upgrading — connected and air-gapped upgrades.
- Self-hosting FAQ — common questions, answered honestly.
- Deciding whether to self-host at all? See SaaS vs self-hosted.