Configuration
Actual Bench needs no environment variables for a basic setup. This page documents the settings you can provide for specific features and deployments.
How configuration is loaded
Section titled “How configuration is loaded”Configuration is provided as environment variables to the container. Server-side variables are read by
the Node server; variables prefixed with NEXT_PUBLIC_ are build/public values exposed to the browser.
The app version is injected automatically from the build — you do not set it.
Environment variables
Section titled “Environment variables”Only variables verified in the current codebase are listed.
| Variable | Default | Purpose | Secret | Restart |
|---|---|---|---|---|
ACTUAL_BENCH_DB_PATH |
/data/actual-bench.sqlite |
Path to the metadata SQLite database. | No | Yes |
DIRECT_BROWSER_API |
enabled | Set to 0 / false / off to disable Direct Actual Server mode. The single, canonical switch — takes effect at runtime on the prebuilt image. |
No | Yes |
NEXT_PUBLIC_DIRECT_BROWSER_API |
(deprecated) | Deprecated alias for DIRECT_BROWSER_API, still honored (with a warning) for one release. Use DIRECT_BROWSER_API instead. |
No | Yes |
SYNC_VAULT_KEY |
(unset) | Enables unattended server-side sync; derives the encryption key for stored credentials. | Yes | Yes |
SYNC_SCHEDULER_SECRET |
(unset) | Enables the external scheduler trigger endpoint; required in its request header. | Yes | Yes |
LOG_LEVEL |
info |
Server log verbosity. | No | Yes |
NEXT_DEV_ALLOWED_ORIGINS |
(unset) | Dev-only: allow the HMR WebSocket through a reverse proxy (comma-separated hostnames). | No | Yes |
Demo/analytics variables (such as NEXT_PUBLIC_ANALYTICS) exist for the public demo build and are inert
in self-hosted deployments — you do not need them.
Minimum configuration
Section titled “Minimum configuration”A basic production deployment needs only a persistent /data volume; no variables are strictly
required. Set ACTUAL_BENCH_DB_PATH only if you want the metadata database somewhere other than the
default.
Direct-mode settings
Section titled “Direct-mode settings”Direct Actual Server mode is enabled by default. To hide it (offer only HTTP API Server mode), set
DIRECT_BROWSER_API=0 and restart. This one server-side runtime variable is read consistently across
the app — the middleware that emits the cross-origin-isolation headers, the connect screen, and the
Actual-engine asset route — so it takes effect on the prebuilt image with no rebuild. See
Deployment for the browser reachability and
cross-origin requirements Direct mode needs.
Metadata database
Section titled “Metadata database”- Default path:
/data/actual-bench.sqlite(override withACTUAL_BENCH_DB_PATH). - Requires a writable persistent volume.
- Schema migrations run on boot; App Health shows the current and latest schema version.
- Stores Actual Bench workflow metadata only — not Actual credentials or budget data, except two explicit, encrypted opt-ins: remembered-connection secrets and unattended-sync credentials (below).
Remembered-server secrets (user passphrase)
Section titled “Remembered-server secrets (user passphrase)”When a user opts in to remembering a server,
its secret is sealed AES-256-GCM in this database, encrypted with a key derived from a passphrase
the user sets — no environment variable is involved. Credentials are server-scoped (one saved server
opens any of its budgets); budget encryption passwords are sealed separately, per budget. The passphrase
and derived key are never stored (only the ciphertext and a salt), so the server cannot decrypt on its
own; the user unlocks once per session. This is separate from the SYNC_VAULT_KEY unattended-sync
vault below — the two never share ciphertext or a key. The feature is only offered when this database is
durable, so it is inert on non-durable runtimes. Back up /data to preserve remembered servers; a lost
passphrase means the user resets the vault and re-saves.
Unattended sync settings
Section titled “Unattended sync settings”For server-side scheduled sync (HTTP API Server mode flows only):
SYNC_VAULT_KEY— a strong operator secret. When set, the in-process scheduler runs and enrolled credentials can be stored/decrypted. When unset, the feature is fully disabled: nothing is persisted and the scheduler never runs. Enrolled credentials are stored AES-256-GCM encrypted in the metadata database; the key is derived from this variable and never stored in the database.SYNC_SCHEDULER_SECRET(optional) — enables an external trigger endpoint atPOST /api/sync/scheduler/tick, which requires the headerx-scheduler-secret. Without it, that endpoint returns403. Use this only if you want to drive the scheduler from an external cron instead of (or in addition to) the built-in one.
Generate strong values with, for example:
openssl rand -base64 48See Budget Sync → Automation for how flows opt in, and
the repository’s docs/UNATTENDED_SYNC.md for the operator walkthrough.
Logging
Section titled “Logging”Set LOG_LEVEL to control server log verbosity (default info). Lower it to warn or error in
production if you want quieter logs, or raise it for debugging.
Secret handling
Section titled “Secret handling”- Never commit a
.envfile with real secrets to version control. - Use your container platform’s secret management for
SYNC_VAULT_KEYandSYNC_SCHEDULER_SECRET. - Do not expose server secrets as
NEXT_PUBLIC_*variables — those are shipped to the browser. - Redact secrets before sharing logs, and rotate any value you suspect is compromised.
Configuration examples
Section titled “Configuration examples”Minimal (Direct or HTTP mode, no unattended sync):
environment: ACTUAL_BENCH_DB_PATH: /data/actual-bench.sqliteWith unattended server-side sync:
environment: ACTUAL_BENCH_DB_PATH: /data/actual-bench.sqlite SYNC_VAULT_KEY: ${SYNC_VAULT_KEY} # provided via your secret managerHTTP API Server mode only (Direct disabled):
environment: DIRECT_BROWSER_API: "0"Use placeholders and inject real secrets from your platform — never hard-code them.
Validation
Section titled “Validation”After changing configuration and restarting, open App Health to confirm the metadata database is writable, the schema is current, and (if configured) the unattended scheduler reports the expected vault state.
