Environment Variable Reference
Bliss uses a single root .env file as the source of truth for all services. Both the Next.js API layer and the Express backend service read from this shared file, ensuring configuration stays consistent across the stack.
How it works
- Docker Compose reads the root
.envautomatically — noenv_filedirective needed. scripts/setup.shgenerates cryptographic secrets (ENCRYPTION_SECRET,JWT_SECRET_CURRENT,NEXTAUTH_SECRET,INTERNAL_API_KEY) so you never have to create them by hand. Run it once after copying.env.exampleto.env.- Optional integrations (Plaid, Twelve Data, CurrencyLayer, Sentry) are activated simply by adding the relevant API keys. The application degrades gracefully when they are absent. The LLM provider (Gemini, OpenAI, or Anthropic) is the one required integration — without it AI classification and insights are unavailable.
- Test-specific overrides: each service may have a
.env.testfile that overridesDATABASE_URLto point at an isolated test database. These files are loaded automatically by the test runners and should never be committed.
Minimal Docker Setup
For a basic Docker deployment, scripts/setup.sh generates all required secrets automatically (ENCRYPTION_SECRET, JWT_SECRET_CURRENT, NEXTAUTH_SECRET, INTERNAL_API_KEY). It also sets safe defaults for DATABASE_URL, REDIS_URL, and all service URLs. You do not need to configure anything to get a working local instance.
The one required integration is an LLM provider for AI classification and insights. The optional ones add useful features but degrade gracefully:
| Integration | Required? | Variables | Purpose |
|---|---|---|---|
| LLM (Gemini / OpenAI / Anthropic) | Yes | LLM_PROVIDER, GEMINI_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY | AI classification + insights |
| Plaid | No | PLAID_CLIENT_ID, PLAID_SECRET | Bank account linking |
| Market data | No | TWELVE_DATA_API_KEY | Stock price fetching |
| Currency rates | No | CURRENCYLAYER_API_KEY | Automatic FX rate fetching |
| Observability | No | SENTRY_DSN | Error tracking |
See Choosing Your External Services for picking and configuring an LLM. Optional integrations can be added any time.
Database
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL | Yes | postgresql://bliss:changeme@localhost:5432/bliss | PostgreSQL connection string used by Prisma in both services. Inside Docker the host is postgres instead of localhost. |
POSTGRES_PASSWORD | Yes | changeme | Superuser password passed to the postgres Docker image to bootstrap the database. |
Redis
| Variable | Required | Default | Description |
|---|---|---|---|
REDIS_URL | Yes | redis://:changeme@localhost:6379 | Full Redis connection URL (include password if set). Used by BullMQ queues and workers. |
REDIS_PASSWORD | Yes | changeme | Password for the redis Docker service. Must match the password embedded in REDIS_URL. |
REDIS_SKIP_TLS_CHECK | No | false | Set to true when Redis uses TLS with a self-signed certificate. For managed services like Upstash, leave as false. |
Secrets
All four required secrets in this section are generated by scripts/setup.sh.
| Variable | Required | Default | Description |
|---|---|---|---|
ENCRYPTION_SECRET | Yes | — | AES-256-GCM key used to encrypt PII fields (transaction descriptions, account numbers, Plaid access tokens). Minimum 32 characters. |
ENCRYPTION_SECRET_PREVIOUS | No | — | Set during encryption key rotation. The application will try the previous key when decryption with the current key fails. Remove once migration is complete. |
JWT_SECRET_CURRENT | Yes | — | HMAC secret used to sign and verify JWT access tokens. |
JWT_SECRET_PREVIOUS | No | — | Set during JWT secret rotation. Tokens signed with the previous secret remain valid until they expire. |
NEXTAUTH_SECRET | Yes | — | Secret used by NextAuth.js for session cookie signing. |
INTERNAL_API_KEY | Yes | — | Shared secret for server-to-server calls between the API layer and the backend service. Must be identical in both services. |
API Layer (Next.js)
| Variable | Required | Default | Description |
|---|---|---|---|
NEXTAUTH_URL | Yes | http://localhost:3000 | Full public URL of the Next.js API layer. Used by NextAuth for callback URLs. |
BACKEND_URL | Yes | http://localhost:3001 | Internal URL of the Express backend service. Used for server-to-server event dispatch and proxy routes. Not exposed to the browser. |
COOKIE_DOMAIN | No | — | Cookie domain for cross-subdomain auth (e.g., .bliss.finance). Leave empty for localhost development. |
Backend Service (Express)
| Variable | Required | Default | Description |
|---|---|---|---|
PORT | No | 3001 | Port the Express backend listens on. |
ALLOWED_ORIGINS | No | http://localhost:3000 | Comma-separated list of allowed CORS origins for the backend. |
Storage
File upload storage is pluggable. The default is local disk; switch to Google Cloud Storage by setting STORAGE_BACKEND=gcs and providing the GCS variables.
| Variable | Required | Default | Description |
|---|---|---|---|
STORAGE_BACKEND | No | local | local for filesystem storage, gcs for Google Cloud Storage. |
LOCAL_STORAGE_DIR | No | ./data/uploads | Directory for uploaded files when using local storage. |
GCS_BUCKET_NAME | No | — | GCS bucket name. Required when STORAGE_BACKEND=gcs. |
GCS_SERVICE_ACCOUNT_JSON | No | — | GCS service account credentials as a JSON string. Required when STORAGE_BACKEND=gcs. |
Frontend
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_API_URL | Yes | http://localhost:3000 | Public URL of the API layer, baked into the web bundle at build time. Changing it requires a rebuild. |
FRONTEND_URL | Yes | http://localhost:8080 | Public URL of the frontend app. Used by the API layer for CORS whitelisting. |
Plaid (optional)
Plaid integration is optional. CSV import works without it. To enable bank-account linking, provide a Plaid client ID and secret.
| Variable | Required | Default | Description |
|---|---|---|---|
PLAID_CLIENT_ID | No | — | Plaid API client ID. |
PLAID_SECRET | No | — | Plaid API secret. |
PLAID_ENV | No | sandbox | Plaid environment: sandbox, development, or production. |
PLAID_WEBHOOK_URL | No | — | Public URL for Plaid webhook delivery. Required in production to receive transaction updates. |
PLAID_HISTORY_DAYS | No | 1 | Default number of days of transaction history fetched on initial Plaid link. Written to Tenant.plaidHistoryDays at signup. |
AI / LLM Provider
Bliss supports three LLM providers for transaction classification and financial insights: Google Gemini, OpenAI, and Anthropic Claude. An LLM provider is required for AI classification and insights. Pick one in .env.
See the Choosing Your External Services guide for per-provider setup, model overrides, and switching workflows.
| Variable | Required | Default | Description |
|---|---|---|---|
LLM_PROVIDER | Yes | gemini | Which provider powers classification and insights. One of: gemini, openai, anthropic. |
EMBEDDING_PROVIDER | When LLM_PROVIDER=anthropic | (follows LLM_PROVIDER) | Override embedding provider. Required when using Anthropic because Anthropic has no embedding API. Must be gemini or openai. |
GEMINI_API_KEY | When LLM_PROVIDER=gemini (or EMBEDDING_PROVIDER=gemini) | — | Google Gemini API key. |
OPENAI_API_KEY | When LLM_PROVIDER=openai (or EMBEDDING_PROVIDER=openai) | — | OpenAI API key. |
ANTHROPIC_API_KEY | When LLM_PROVIDER=anthropic | — | Anthropic API key. |
EMBEDDING_MODEL | No | gemini-embedding-001 / text-embedding-3-small / (n/a) | Override the embedding model for the active embedding provider. Output is projected to 768 dimensions. |
CLASSIFICATION_MODEL | No | gemini-3-flash-preview / gpt-4.1-mini / claude-sonnet-4-6 | Override the classification model for the active LLM provider. |
INSIGHT_MODEL | No | gemini-3.1-pro-preview / gpt-4.1 / claude-sonnet-4-6 | Override the insights model for the active LLM provider. |
Graceful degradation: Missing the primary LLM provider key produces a warning and disables AI classification + insights (the rest of the app keeps working). Missing the secondary EMBEDDING_PROVIDER key when you opted in explicitly is a hard startup error.
Market Data (optional)
Stock price fetching is provided by Twelve Data by default and is disabled when no API key is set.
| Variable | Required | Default | Description |
|---|---|---|---|
STOCK_PROVIDER | No | TWELVE_DATA | Stock data provider. Supported values: TWELVE_DATA (recommended), FINNHUB. |
TWELVE_DATA_API_KEY | No | — | API key for Twelve Data. Enables real-time and historical pricing for stocks, ETFs, and funds (10,000+ symbols across 27+ markets). |
FINNHUB_API_KEY | No | — | API key for Finnhub. Alternative stock data provider. |
Currency Rates (optional)
Automatic historical exchange rate fetching is disabled when the API key is not set. Rates can still be entered manually via the UI.
| Variable | Required | Default | Description |
|---|---|---|---|
CURRENCYLAYER_API_KEY | No | — | API key for CurrencyLayer . Used by portfolio processing and analytics to fetch historical FX rates. |
Observability (optional)
Error tracking and performance monitoring via Sentry. Leave all three unset to disable.
| Variable | Required | Default | Description |
|---|---|---|---|
SENTRY_DSN | No | — | Sentry Data Source Name. Enables error reporting when set. |
SENTRY_ORG | No | — | Sentry organization slug. Used for source-map uploads during CI builds. |
SENTRY_PROJECT | No | — | Sentry project slug. Used alongside SENTRY_ORG for source-map uploads. |