Vestyn runs as a Docker stack with Postgres behind your HTTPS reverse proxy. Put it on a VPS, home server, or NAS — wherever you want your vault data to live.
A host with Docker Compose v2, roughly 1GB RAM, and a domain you can point at it. amd64 and arm64 both work.
The first account registered on a new instance becomes the admin — every account after that needs an invite.
Pin VESTYN_IMAGE_TAG to a release, update deliberately, and back up Postgres plus the signing key.
Vestyn ships as two published container images plus Postgres — copy the two files below onto your host and bring the stack up. A note on source availability: Vestyn is licensed under AGPL-3.0, but the source repository is not yet public while the project is in closed alpha — we're doing a security/history review before opening it up. Pre-built images with build provenance and SBOM attestations are published today so you can self-host in the meantime — the exact source that built the current release is downloadable as a Corresponding Source archive.
Make a directory for the stack and save the compose file into it below. Both files must sit in the same directory — Compose reads .env from the compose file's folder, not from your shell's current folder.
Save this as docker-compose.yml:
Corresponding Source for the pinned server/web images above: download the matching source archive.
Save this as .env, next to the compose file. POSTGRES_PASSWORD is the only required value — the stack refuses to start without it.
Check the changelog for the latest published version, and keep server and web on the same tag.
Compose pulls the images, starts Postgres, waits for it to become healthy, then starts the API and the web front end. The API runs its database migrations automatically on boot — there is no separate migration step, now or on upgrade.
Once api and web report healthy, the vault is live on http://localhost:8080. Put TLS in front before creating an account — your master password is typed into that page.
The web container speaks plain HTTP on the published port. TLS is your reverse proxy's job. Caddy gets you automatic certificates in two lines:
Traefik and nginx Proxy Manager work equally well — point them at the same port. Then set APP_URL in .env to your real origin so invite links use the right hostname, and restart the API to pick it up:
Traffic between your proxy and the Vestyn container is plain HTTP inside the Docker network — the standard self-host pattern, safe when both run on the same host or a trusted private network. The server only ever holds ciphertext regardless. How the encryption works →
Open your domain in a browser and register. The first account created on a fresh instance becomes the admin — and it is the only account that can ever be created without an invite. Make it yours, not a test account.
To add other people: sign in as the admin, go to Settings → Members & Invites, and create an invite link. Links carry a one-time token, expire after 7 days, and work exactly once. Registration without a valid invite is rejected by the server.
Then open the Mac app and point it at your host on first launch. Download the apps →
Set VESTYN_IMAGE_TAG in .env to the version you want, then pull and recreate. Keep both images on the same tag.
Migrations run automatically when the new API container boots. Take a database dump before upgrading a production instance — see below.
Two named volumes hold everything that matters. Vault contents are encrypted on your devices, but a lost database is still a lost vault — the server cannot reconstruct it.
| Volume | Contents |
|---|---|
| vestyn_db-data PostgreSQL | Accounts, vault membership, and every encrypted item. Losing this loses the vault. |
| vestyn_api-data signing.key | Signs access tokens. Losing it logs everyone out; it loses no data. |
Dump the database on a schedule and keep the dumps somewhere other than this host. --clean --if-exists makes the dump drop existing objects before recreating them, so restoring doesn't collide with the schema migrations already put there:
Back up the signing key once — it does not change:
Restoring it needs a helper container rather than docker compose cp, since the file has to land in the volume with the ownership the server's non-root user expects:
Restore the database and every account and vault comes back. Restore the signing key too and active sessions survive; skip it and everyone simply logs in again — no data loss either way. Rehearse the restore before you need it.
docker compose down stops the stack and keeps your data. docker compose down -v deletes the volumes and everything in them — there is no undo.