Skip to content

Recipe: a plain VPS

Vaultbase on one Linux server from scratch, with HTTPS, real client IPs, backups and updates.

One Ubuntu 24.04 server running Vaultbase as a service, with Caddy in front for HTTPS. It takes about fifteen minutes. The deployment guide explains each piece in more depth; this page is the order to do them in.

You need:

  • A VPS with a public IPv4 address. 1 vCPU and 1 GB of RAM is enough to start.
  • A domain, here api.example.com, with an A record pointing at the server (and an AAAA record if the server has IPv6).
  • SSH access as a user that can run sudo.
Terminal window
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw enable

Vaultbase listens on port 8091, which stays closed: Caddy is the only way in.

Terminal window
curl -fsSL https://get.vaultbase.dev | sudo sh -s -- --verify-sig

The script installs the binary to /usr/local/bin/vaultbase and creates a vaultbase system user. It writes /etc/vaultbase/vaultbase.env with a fresh JWT secret and setup key, and starts a hardened systemd service. Data lives in /var/lib/vaultbase. --verify-sig requires the release’s cosign signature check, and fails if cosign isn’t installed (without the flag the check runs whenever cosign is there). See Installation for the other flags.

Edit /etc/vaultbase/vaultbase.env and add:

Terminal window
# Listen on loopback only: Caddy, on the same machine, is the only client.
VAULTBASE_HOST=127.0.0.1
# Trust X-Forwarded-For from Caddy, so rate limits, sign-in lockouts and
# the audit log see the caller's address instead of 127.0.0.1.
VAULTBASE_TRUSTED_PROXIES=127.0.0.1/32,::1/128

The server made an encryption key at its first start, in /var/lib/vaultbase/.encryption-key. It encrypts secret settings and encrypted fields. Back it up with the database: without it those values can’t be read. (To keep your own key in the env file instead, set VAULTBASE_ENCRYPTION_KEY now, while nothing has been encrypted yet.)

Then restart it:

Terminal window
sudo systemctl restart vaultbase
curl -s http://127.0.0.1:8091/_/health

If you’d rather use Postgres than SQLite, set VAULTBASE_DATABASE_URL now, before you create the first admin. The choice is made once per install. See Postgres.

Install Caddy from its own package repository (Ubuntu’s package is older than the {client_ip} placeholder used below):

Terminal window
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy

Replace /etc/caddy/Caddyfile with:

api.example.com {
reverse_proxy 127.0.0.1:8091 {
# The address Caddy saw, set (not appended), so a client can't forge it.
header_up X-Forwarded-For {client_ip}
}
}
Terminal window
sudo systemctl reload caddy

Caddy gets a Let’s Encrypt certificate for the domain on the first request and renews it. It sets X-Forwarded-Proto itself, so sign-in cookies get the Secure flag. It proxies the realtime WebSocket (/realtime) and the SSE stream (/api/v1/realtime) without extra settings.

Check it from your own machine:

Terminal window
curl -s https://api.example.com/_/health
curl -s https://api.example.com/_/ready

On the server, without exposing anything:

Terminal window
sudo -u vaultbase vaultbase setup-admin --email [email protected] --password '<a long password>'

Run it as the vaultbase user, as the service runs: a file it writes as root in the data directory would stop the service from starting.

Then sign in at https://api.example.com/_/. (The browser setup page works too; it asks for the setup key from /etc/vaultbase/vaultbase.env.)

In the admin, open Settings → CORS and add the origins of your web apps to the allowed origins, so browsers may call the API and connect to realtime.

In the admin, Settings → Backup & restore schedules backups of the database. Send them to S3-compatible storage, so a lost server doesn’t take its backups with it. Also keep a copy of /etc/vaultbase/vaultbase.env and /var/lib/vaultbase/.encryption-key off the server: they hold the JWT secret and the encryption key, and a backup without the key can’t decrypt encrypted values. See Backups.

With local file storage, scheduled backups also keep the uploaded files (since 0.34.0): each file is stored once, and a restore puts the missing ones back. With the S3 copy on, they go to the bucket too.

Terminal window
sudo vaultbase update --check # is a newer release out?
sudo vaultbase update --yes && sudo systemctl restart vaultbase

Read the release notes before a minor version (0.x.0); they list anything to do first. Self-update shows how to run it from cron.

  • journalctl -u vaultbase -f shows the server’s log; the structured logs are in /var/lib/vaultbase/logs/.
  • sudo -u vaultbase vaultbase doctor checks the configuration and the database.
  • 502 from Caddy: Vaultbase isn’t running or isn’t on 127.0.0.1:8091 (systemctl status vaultbase).
  • Every request in the audit log comes from 127.0.0.1: VAULTBASE_TRUSTED_PROXIES is missing, or the service wasn’t restarted. A value under Settings → Security → Trusted proxies in the admin takes precedence over the env var, so check it too.
  • The service won’t start and the log says database.json can't be read: a command was run as root in the data directory. Run sudo chown -R vaultbase:vaultbase /var/lib/vaultbase and start it.