Configuration reference

Every setting for the API and the web app, with defaults.

7 min read Edit this page

This page lists every Secure Vault setting, its default and what it does. Backend settings live in backend/.env in development and in .env.prod with the production compose file. Frontend settings are only needed for development and for building the public site.

How settings are loaded

The API reads its settings from environment variables and, in development, from a .env file in the directory it runs from (backend/). Start from the documented example:

bash
cp backend/.env.example backend/.env

The dev scripts do this for you the first time, with a random SECRET_KEY.

Every setting also works as a plain environment variable with the same name, for example SMTP_HOST=mail.example.com. Environment variables take precedence over the .env file. Names aren't case-sensitive, but the docs use upper case throughout.

Boolean settings accept true or false. Settings without a default in the tables below are empty unless you set them.

General

SettingDefaultWhat it does
ENVIRONMENTdevelopmentdevelopment, test or production. production turns off the interactive API docs (/api/docs) and the OpenAPI schema. test is for the test suite and doesn't start the email worker. The production compose file sets production for you.
APP_URLhttp://localhost:29180The public origin of the web app, exactly as it appears in the browser's address bar. Used for links in emails, the CSRF Origin check, CORS and the Google sign-in redirect URI.
APP_NAMESecure VaultThe name shown in emails, at the top of each message and in the signature line.

Database

SettingDefaultWhat it does
DATABASE_URLpostgresql+asyncpg://vault:vault@localhost:29432/vaultPostgreSQL connection URL. The default matches the development docker-compose.yml. The production compose file builds this for you from POSTGRES_PASSWORD. Plain postgresql://...?sslmode=require URLs from hosted providers such as Neon also work: they're converted to the postgresql+asyncpg:// form automatically.
DATABASE_ECHOfalseLogs every SQL statement. Useful for debugging, too noisy otherwise.

Security and sessions

SettingDefaultWhat it does
SECRET_KEYAn insecure development valueSigns the short-lived state cookie used during Google sign-in. Must be long and random in production. Generate one with python3 -c "import secrets;print(secrets.token_urlsafe(48))".
COOKIE_SECUREfalseSet to true whenever the app is served over HTTPS, which production always should be. It marks cookies Secure, gives them the __Host- prefix and makes the API send HSTS.
SESSION_TTL_DAYS30Absolute session lifetime. After this many days you have to sign in again, however active you are.
SESSION_IDLE_DAYS7A session that isn't used for this many days expires.

These settings only affect sign-in sessions. The vault's auto-lock isn't a server setting: each person picks it under Lock automatically after in their vault settings, and it's remembered per browser.

Email

SettingDefaultWhat it does
SMTP_HOSTlocalhostSMTP server host name.
SMTP_PORT29025SMTP server port. The default is Mailpit from the development docker-compose.yml.
SMTP_USERNAMESMTP user name. Leave it empty if your server doesn't need authentication.
SMTP_PASSWORDSMTP password.
SMTP_STARTTLSfalseConnects in plain text, then upgrades with STARTTLS. Usually port 587.
SMTP_TLSfalseUses TLS from the first byte (implicit TLS). Usually port 465. Turn on at most one of SMTP_STARTTLS and SMTP_TLS.
MAIL_FROMSecure Vault <no-reply@vault.local>The sender of every email, as Name <address>.
EMAIL_WORKER_ENABLEDtrueRuns the background worker that delivers queued emails and does housekeeping, such as removing expired sessions and expiring old invitations. If it's off, emails stay queued and are never sent.

Emails are written to an outbox in the database first, then delivered by the worker, which retries failed deliveries with increasing delays. See Email and Google sign-in.

Google sign-in

SettingDefaultWhat it does
GOOGLE_CLIENT_IDOAuth client ID from Google Cloud.
GOOGLE_CLIENT_SECRETOAuth client secret.

Google sign-in is on only when both are set. Otherwise the Google buttons are hidden. Register <APP_URL>/api/auth/google/callback as the authorised redirect URI.

SettingDefaultWhat it does
EMAIL_VERIFICATION_TTL_HOURS48How long an email verification link works.
PASSWORD_RESET_TTL_MINUTES60How long a password reset link works. Each link works once.
INVITATION_TTL_DAYS7How long a workspace invitation stays open before it expires.

Rate limits

Rate limits are stored in PostgreSQL, so they hold across restarts and across several API workers.

SettingDefaultWhat it does
SIGNIN_IP_LIMIT30Sign-in attempts allowed from one IP address per sign-in window.
SIGNIN_EMAIL_FAILURE_LIMIT5Failed sign-ins allowed for one email address per sign-in window. After that, the address is locked out until the window passes. It behaves the same for addresses that don't have an account.
SIGNIN_WINDOW_MINUTES15Length of the sign-in window, in minutes.
EMAIL_ACTION_LIMIT5Sign-ups, verification resends and password reset requests allowed for one email address per email-action window. One IP address may make four times as many.
EMAIL_ACTION_WINDOW_MINUTES30Length of the email-action window, in minutes.
TOKEN_SUBMIT_IP_LIMIT30Verification and password reset links that one IP address may submit per 15 minutes.

When a limit is hit, the user sees "Too many attempts. Please try again in about N minutes."

Uploads

SettingDefaultWhat it does
MAX_AVATAR_BYTES1000000Largest profile picture you can upload, in bytes (about 1 MB).

Serverless hosting

These settings are for platforms that pause the API between requests, such as the Vercel demo. Leave them unset when you self-host: the defaults keep the normal long-running server behaviour.

SettingDefaultWhat it does
SERVERLESSfalseDoesn't start the email worker and housekeeping loops. Instead, each email is sent before the request that queued it returns, and database connections are opened per request instead of pooled, so they work through a transaction-mode pooler such as Neon's.
CRON_SECRETTurns on GET /api/internal/cron, which retries unsent emails and does housekeeping once. Callers must send Authorization: Bearer <CRON_SECRET>. Without it, the endpoint returns 404.
CLIENT_IP_HEADERA request header holding the visitor's IP address, set by a proxy you trust, for example x-real-ip on Vercel. Used for rate limits, the audit log and the sessions list. Set it only if every request passes through that proxy, otherwise clients could fake their IP. When unset, the connection's address is used, which behind the bundled nginx is already the real client IP.

Production compose settings

The production compose file reads two more variables from .env.prod. They configure the containers, not the API.

SettingDefaultWhat it does
POSTGRES_PASSWORDNone, requiredPassword for the vault database user. Compose refuses to start without it. Use letters and digits only, because it's placed inside a URL.
WEB_PORT29080Host port where the web container (nginx) is published. Put your TLS reverse proxy in front of it.

See Production for how these fit together.

Frontend settings

The browser app has no runtime settings or secrets: it always talks to the API on its own origin, under /api. The frontend settings below are read by the Vite dev server and by the build.

SettingDefaultWhat it does
API_PROXY_TARGEThttp://127.0.0.1:29100Where the Vite dev server (npm run dev) and preview server (npm run preview) forward /api requests. Change it if your API runs on another port or host.
VITE_SITE_URLThe absolute public origin of the site, for example https://vault.example.com. Used for canonical and Open Graph URLs and for the sitemap.
VITE_DEMO_MODESet to true to show the public-demo notices on the landing page and in the docs. Leave it unset on your own deployment.

API_PROXY_TARGET is read from the environment when the dev or preview server starts:

bash
API_PROXY_TARGET=http://127.0.0.1:29101 npm run dev

VITE_SITE_URL and VITE_DEMO_MODE are build-time settings. They're baked into the files when you run npm run build, so changing them later requires a rebuild. Set them in the environment for the build, or in frontend/.env.production, which Vite reads during a production build:

env
VITE_SITE_URL=https://vault.example.com
VITE_DEMO_MODE=false

Enjoying Secure Vault?

A star on GitHub helps other teams find it, and keeps the project going.

Star on GitHub

Search the docs

Find a page or a section