Skip to main content

Environment Variables

This reference is based on these source files in the cashlytics-ai repository:

  • .env.example
  • docker-compose.selfhost.yml (production self-hosting)
  • docker-compose.yml (local build and development database)
  • docker-compose.demo.yml (demo stack)
  • Dockerfile

Required baseline for self-hosting​

Set these before first boot:

POSTGRES_PASSWORD=replace_with_long_random_password
AUTH_SECRET=replace_with_long_random_secret
CRON_SECRET=replace_with_long_random_secret
NEXT_PUBLIC_APP_URL=https://your-domain.tld

AUTH_TRUST_HOST (default true) and SINGLE_USER_MODE (default true) have Compose defaults and can be omitted.

Full variable reference​

VariableRequiredDefaultPurposeRecommended value
DATABASE_URLYes (manual and local setup)Built in docker-compose.selfhost.yml from POSTGRES_PASSWORD; the .env value is ignored therePostgreSQL connection string used by the app and the migrationspostgresql://cashlytics:<password>@postgres:5432/cashlytics (Docker), ...@localhost:5432/... (local dev)
POSTGRES_PASSWORDYes (self-host)None (demo: cashlytics_demo)Password for the PostgreSQL container user. Ignored by docker-compose.yml, which uses a fixed cashlytics_secretLong random secret (32+ chars)
NEXT_PUBLIC_APP_URLStrongly recommendedhttp://localhost:3000 in ComposeBase URL for links in emails (fallback for APP_URL). Compiled in at build time, so it cannot change client code of the published image. Authentication uses the request host (AUTH_TRUST_HOST), not this valueYour real HTTPS domain
NEXT_PUBLIC_DEFAULT_LOCALENodeFallback language for new users. Build time only, see belowen or de
NEXT_PUBLIC_DEFAULT_CURRENCYNoEURFallback currency for new users. Build time only, see belowEUR, USD, GBP, CHF
AUTH_SECRETYesNone (demo: insecure default)Auth.js secret for tokens/session securityStrong random secret (openssl rand -base64 32)
AUTH_TRUST_HOSTStrongly recommendedtrue in the self-host and demo Compose files; not set by docker-compose.ymlTrust the request host behind a proxy or domain. Read by Auth.js itselftrue for VPS/reverse proxy
SINGLE_USER_MODENotrue in Compose; unset means open registrationtrue: only the first account can register (registration closes once a user exists). false or unset: open registrationtrue for personal use
OPENAI_API_KEYNoEmptyEnables AI Assistant, Receipt Scanner, and AI reconciliation in CSV Import. Without it these features are hidden; CSV import still works without AI matchingValid OpenAI API key
EMAIL_TRANSPORTNoEmptyOnly the value sendmail has an effect (local /usr/sbin/sendmail). Any other value, including smtp, falls back to the SMTP variablesLeave empty for SMTP
SMTP_HOSTNo*EmptySMTP server hostnameProvider hostname
SMTP_PORTNo*EmptySMTP server port (must be a number)587 (STARTTLS) or 465 (TLS)
SMTP_USERNo*EmptySMTP auth usernameSMTP account username
SMTP_PASSNo*EmptySMTP auth password/tokenApp password or provider token
SMTP_FROMNoSMTP_USER; in sendmail mode then noreply@localhostSender address for outgoing mailsnoreply@your-domain.tld
APP_URLNoNEXT_PUBLIC_APP_URL, then http://localhost:3000Base URL for links in emails (read at runtime). See "Two meanings of APP_URL"Same as public app URL
VAPID_PUBLIC_KEYNo**EmptyPublic key for browser push subscriptionnpx web-push generate-vapid-keys
VAPID_PRIVATE_KEYNo**EmptyPrivate key to sign push messagesnpx web-push generate-vapid-keys
VAPID_SUBJECTNo**EmptyContact URI for VAPID identitymailto:ops@your-domain.tld
CRON_SECRETYes (self-host stack)EmptyBearer token for /api/cron/upcoming-payments and /api/cron/insights. Without it both routes return HTTP 500openssl rand -hex 32
NOTIFICATION_SCHEDULENo0 8 * * *Cron schedule (UTC) for the upcoming-payments job. Read only by the cron container, not by the appKeep default or adjust
INSIGHTS_SCHEDULENo0 6 * * *Cron schedule (UTC) for the daily assistant insights job. Read only by the cron container, not by the appKeep default or adjust

* SMTP is only active when SMTP_HOST, SMTP_PORT, SMTP_USER, and SMTP_PASS are all set. Otherwise email is off and nobody can reset a password by email.
** Push is only active when VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_SUBJECT are all set; otherwise the app answers HTTP 503 for the VAPID public key.

The self-host Compose file always starts the cron container, which uses CRON_SECRET for both jobs, so treat it as required. It is only optional if you remove the cron container and do not use reminders or insights.

Build-time variables​

NEXT_PUBLIC_* values are compiled into the client bundle when the image is built. The published image ghcr.io/aaronjoeldev/cashlytics-ai is built with de and EUR, and setting these variables on the container does not change that (docker-compose.selfhost.yml therefore does not pass them). Users choose language and currency in the settings. To change the defaults, build your own image; see Docker and Build.

Two meanings of APP_URL​

  • In the app container, APP_URL is the public base URL used for links in emails (such as the password reset link).
  • In the cron container of docker-compose.selfhost.yml, APP_URL is set to the internal address http://cashlytics:3000 and is used to call the cron routes. It does not come from your .env.
  • In docker-compose.demo.yml, APP_URL is the public URL and is mapped to NEXT_PUBLIC_APP_URL.

Image and demo variables​

VariableWherePurpose
PORTSet by the imageListening port, 3000
HOSTNAMESet by the imageBind address, 0.0.0.0
NODE_ENVSet by the imageproduction
APP_PORTdocker-compose.demo.yml onlyHost port for the demo app (default 3000)
CHROME_PATHEnd-to-end tests onlyPath to Chrome if it is not found automatically

Minimal (no AI, no SMTP, no push)​

POSTGRES_PASSWORD=...
AUTH_SECRET=...
CRON_SECRET=...
NEXT_PUBLIC_APP_URL=https://app.example.com

AI enabled​

OPENAI_API_KEY=sk-...

SMTP enabled​

SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=mailer@example.com
SMTP_PASS=...
SMTP_FROM=noreply@example.com
APP_URL=https://app.example.com

Push reminders enabled​

VAPID_PUBLIC_KEY=...
VAPID_PRIVATE_KEY=...
VAPID_SUBJECT=mailto:ops@example.com
NOTIFICATION_SCHEDULE=0 8 * * *

Validation and safety checks​

  • Keep .env out of git.
  • Use long random values for POSTGRES_PASSWORD, AUTH_SECRET, and CRON_SECRET.
  • Keep NEXT_PUBLIC_APP_URL and APP_URL aligned with your real domain.
  • If authentication behaves incorrectly behind a proxy, verify AUTH_TRUST_HOST=true.
  • If emails fail, check that all four SMTP variables are set, the port, credentials, and sender domain policies. See Emails.
  • For cron problems, see Scheduled Jobs.