Vigil

Configuration

Environment variables and defaults.

Configuration

The backend reads its configuration from environment variables. There is no configuration file. The Load function in server/internal/config reads the variables and returns a Config value.

Configuration Variables

Variable Purpose Default
NVR_HTTP_ADDR The HTTP listen address. :8080
NVR_DATA_DIR The data directory for the database. ./data
NVR_RECORDINGS_DIR The recordings directory. ./recordings
NVR_SECRETS_KEY The encryption key for secrets. empty
NVR_LOG_LEVEL The log level. info
NVR_RETENTION_DAYS The local fallback retention period in days. Drive-archived files are removed sooner after upload. 7
NVR_ARCHIVE_INTERVAL_SECONDS Fallback cadence for the Drive archive loop when no segment completions arrive. Minimum 15. 300
NVR_MAX_LOCAL_DWELL_MINUTES How long an unarchived segment may stay on the recordings volume before oldest-first eviction. 0 disables. Intended for RAM-staged recordings. 0
NVR_LOCAL_EVICT_THRESHOLD Used-percent of the recordings volume that starts oldest-first overflow eviction of unarchived segments. 0 disables. 0
NVR_MEDIAMTX_API_URL The MediaMTX control API URL. http://127.0.0.1:9997
NVR_MEDIAMTX_WEBRTC_URL The MediaMTX WebRTC URL. http://127.0.0.1:8889
NVR_MEDIAMTX_HLS_URL The MediaMTX HLS URL. http://127.0.0.1:8888
NVR_MEDIAMTX_PLAYBACK_URL The MediaMTX playback URL. empty
NVR_GOOGLE_CLIENT_ID The Google OAuth client ID. empty
NVR_GOOGLE_CLIENT_SECRET The Google OAuth client secret. empty
NVR_GOOGLE_REDIRECT_URL The Google OAuth redirect URL. empty
NVR_PUBLIC_URL The externally reachable HTTPS URL of this server. empty
NVR_HOSTED_DASHBOARD_URL The hosted dashboard URL used to reach this server. empty
NVR_CORS_ORIGINS Extra exact HTTPS origins allowed cross-origin (comma-separated). empty
NVR_ADMIN_USERNAME First-admin username for first-start env bootstrap. empty
NVR_ADMIN_PASSWORD First-admin password for first-start env bootstrap. empty

The recognized log levels are debug, info, warn, warning, and error. A malformed integer falls back to the default value.

The secrets key

The NVR_SECRETS_KEY variable is the encryption key. It encrypts the camera credentials and the Google Drive tokens at rest. Set a long random value in production.

The key is required before you connect Google Drive. Without a key, the backend stores credentials as plaintext for development.

Database settings

The backend stores some settings in the SQLite settings table. These settings override the environment variables. The settings are:

  • The recordings directory.
  • The recording enabled flag.
  • The retention period. It controls non-Drive pruning and MediaMTX's recordDeleteAfter fallback; successful Drive uploads are cleaned up immediately.

When the recordings directory is empty, the backend disables recording.

Public and hosted dashboard URLs

NVR_PUBLIC_URL is the externally reachable HTTPS URL of this recorder. It is used by the slim/headless build to deep-link to the hosted dashboard, and it is the URL the browser reaches for playback. NVR_HOSTED_DASHBOARD_URL points at the separately hosted dashboard that manages this server.

The backend persists both values in the SQLite settings table (keys publicUrl and hostedDashboardUrl), set through nvrd setup or the setup wizard. The environment variables deliberately override the persisted values when non-empty; the persisted database values apply when the environment is unset. There is no third configuration layer.

Both values must be absolute http or https URLs. In production, set NVR_PUBLIC_URL to an HTTPS origin so the browser, HLS, and WebRTC signaling are reachable over the tunnel. See operations for the HTTPS tunnel requirement.

CORS origins

The backend allows cross-origin requests only from exact origins. The effective allow-list is:

  • Every origin in NVR_CORS_ORIGINS (comma-separated). Each must be HTTPS unless it is a localhost development origin (http://localhost:* or http://127.0.0.1:*).
  • The origin derived from NVR_HOSTED_DASHBOARD_URL (the resolved hosted dashboard origin is always allowed).

Configured origins receive Access-Control-Allow-Origin, Access-Control-Allow-Headers (including Authorization and X-Session-Token), Access-Control-Expose-Headers: X-Session-Token, and Vary: Origin, but not credential allowance. Localhost development origins retain credential allowance for the embedded-UI dev flow.

First-admin bootstrap

NVR_ADMIN_USERNAME and NVR_ADMIN_PASSWORD support idempotent first-start automation. They are honored only when the database has no users; once any user exists they are ignored. The username must be non-empty after trimming and the password must contain at least eight runes — invalid partial or weak bootstrap configuration is a startup error while setup is required. The password is argon2id-hashed and never persisted. Remove NVR_ADMIN_PASSWORD from the environment after the first admin is created.

Google Drive configuration

You can set the Google OAuth details in two ways:

  • As the NVR_GOOGLE_* environment variables.
  • In the dashboard under Settings, Google Drive.

When you save the OAuth details in the dashboard, the backend encrypts them and stores them in the settings table. It prefers these persisted values when they are present.

To set up Google OAuth:

  1. Enable the Google Drive API.
  2. Configure the consent screen with the drive.file and userinfo.email scopes.
  3. Create Web application credentials.
  4. Make the authorized redirect URI exactly match NVR_GOOGLE_REDIRECT_URL.

The default redirect URL is:

http://localhost:8080/api/v1/storage/gdrive/callback

RAM-staged recordings

By default, segments land on a normal disk and Drive archival deletes each local file within minutes of its upload. If the recordings volume is an SSD you want to spare, or simply small, you can stage recordings in RAM instead:

  1. Mount a tmpfs volume at the recordings path. In Docker, back the nvr-recordings volume (the Docker volume name) with tmpfs; see the commented block in deploy/docker-compose.yml. Size it to leave headroom for the OS — for example 2–4 GiB on an 8 GiB host.
  2. Set NVR_RECORDINGS_DIR to the container mount path: /var/lib/nvr/recordings (not the volume name). The compose file already mounts the nvr-recordings volume at that path.
  3. Opt in to the safeguards so a full staging volume never stalls recording:
    • NVR_MAX_LOCAL_DWELL_MINUTES evicts unarchived segments older than the window, oldest first.
    • NVR_LOCAL_EVICT_THRESHOLD evicts oldest-first while the volume is above the used-percent (a number 0-100, fractions allowed; 0 disables). The threshold measures the whole recordings filesystem, so give the staging volume its own mount. On a shared mount, unrelated files can pin usage above the threshold and enforcement will warn after evicting without reclaiming space.

With Google Drive connected, every completed one-minute segment nudges an immediate archive pass, so under normal operation a segment exists locally for only a few seconds and the SSD receives no recording writes at all (when host swap is disabled; if swap is enabled, tmpfs pages may be swapped to disk). The SQLite index stays on NVR_DATA_DIR.

Trade-offs to accept before enabling this mode:

  • A reboot, power cut, or Docker daemon restart empties tmpfs. Everything not yet uploaded is gone.
  • If Drive is unavailable longer than the dwell window, footage starts dropping oldest-first. Evicted rows stay in segment list results marked skipped:expired (but are excluded from day availability) until retention pruning removes them; evictions are reported in the server logs.
  • Upload bandwidth must keep up with recording bitrate. When it does not, eviction — not the archive backlog — defines what you lose.

The example environment file

The example file is in deploy/nvr.example.env. Copy it and set your values. The file documents the active variables and the commented production options.

See operations for the Docker environment and the deployment model.