> ## Documentation Index
> Fetch the complete documentation index at: https://atrium.vibralabs.co/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Environment Variables

> All configuration for Atrium is controlled through environment variables, passed via Docker or a .env file.

Every Atrium setting lives in an environment variable. In a Docker deployment you pass them with `-e` flags or an `environment:` block in your Compose file. When running from source, place them in a `.env` file at the project root.

Only two variables are required to start Atrium. Everything else has a sensible default.

> [!WARNING]
>
> The API will refuse to start if `DATABASE_URL` or `BETTER_AUTH_SECRET` is missing. In production, it will also refuse to start if `BETTER_AUTH_SECRET` is the default placeholder value or shorter than 32 characters.

***

## App URLs

By default Atrium auto-detects its URLs. You only need to set these explicitly when your API and web app are deployed on separate hostnames, or when Atrium is running behind a reverse proxy on a non-standard port.

**WEB\_URL** `string` (default: [http://localhost:3000](http://localhost:3000))

Public URL of the web frontend. The API uses this for CORS and Content Security Policy. Set this to the domain where your users access Atrium (e.g., `https://portal.yourcompany.com`).

**API\_URL** `string` (default: [http://localhost:3001](http://localhost:3001))

Internal URL the web server uses to call the API server-to-server. In a single-container deployment this stays at the default. In a split deployment, point this at your API service.

**NEXT\_PUBLIC\_API\_URL** `string` (default: [http://localhost:3001](http://localhost:3001))

Browser-facing API URL. Must be reachable from your users' browsers, so set this to a public hostname in production split-deployment setups.

> [!TIP]
>
> The official Docker image bundles both the API and the web app behind a Caddy reverse proxy on port 8080. In that setup you do not need to set any URL variables — Caddy handles internal routing automatically.

***

## Database

**DATABASE\_URL** `string` *required*

PostgreSQL connection string. Format: `postgresql://user:password@host:5432/dbname?schema=public`

When using the built-in database (`USE_BUILT_IN_DB=true`, the default), this is generated automatically and you can leave it unset. When connecting to an external database you must provide it.

**DIRECT\_URL** `string`

A non-pooled (direct) connection string used exclusively for schema migrations. Required when your `DATABASE_URL` points to a connection pooler such as PgBouncer or Supabase's Transaction Mode pooler, which don't support the `CREATE INDEX CONCURRENTLY` statements that migrations use.

```bash theme={null}
DATABASE_URL="postgresql://user:password@pgbouncer:6432/atrium"
DIRECT_URL="postgresql://user:password@postgres:5432/atrium"
```

**USE\_BUILT\_IN\_DB** `boolean` (default: true)

Controls whether the Docker image starts its bundled PostgreSQL instance. Set to `false` and provide `DATABASE_URL` when you want to use your own database server.

**SKIP\_DB\_PUSH** `boolean` (default: false)

When `true`, Atrium skips the automatic schema sync that runs on startup. Use this when your database connection goes through a pooler that doesn't support schema migrations (e.g., PgBouncer). Provide a `DIRECT_URL` for a non-pooled connection so schema sync can run separately.

***

## Auth

**BETTER\_AUTH\_SECRET** `string` *required*

Secret key used to sign and verify authentication tokens. Must be at least 32 characters long and unique per deployment. The API will not start in production if this is set to the default placeholder value.

Generate a suitable value with:

```bash theme={null}
openssl rand -base64 32
```

> [!WARNING]
>
> Never share or commit your `BETTER_AUTH_SECRET`. If it is ever exposed, rotate it immediately — all active sessions will be invalidated.

***

## Storage

See [File Storage](/docs/configuration/storage) for a full guide on each backend.

**STORAGE\_PROVIDER** `string` (default: local)

The file storage backend to use. Accepted values: `local`, `s3`, `minio`, `r2`.

**UPLOAD\_DIR** `string` (default: ./uploads)

Directory where files are stored when `STORAGE_PROVIDER=local`. Must be a mounted Docker volume to survive container restarts.

**S3\_BUCKET** `string` (default: atrium)

Name of the S3 (or S3-compatible) bucket. Used by the `s3`, `minio`, and `r2` providers.

**S3\_REGION** `string` (default: us-east-1)

AWS region of the bucket. For MinIO and R2 this is still required by the SDK but the value is not significant — `us-east-1` works.

**S3\_ENDPOINT** `string`

Custom S3-compatible endpoint URL. Required for `minio` and `r2`. Not used for the standard `s3` provider. Example: `https://your-account.r2.cloudflarestorage.com`.

**S3\_ACCESS\_KEY** `string`

Access key ID for the storage provider. Required for `s3`, `minio`, and `r2`.

**S3\_SECRET\_KEY** `string`

Secret access key for the storage provider. Required for `s3`, `minio`, and `r2`.

**MAX\_FILE\_SIZE\_MB** `number` (default: 50)

Maximum file size allowed for uploads, in megabytes. Applies to all storage backends.

***

## Email

See [Email Setup](/docs/configuration/email) for a step-by-step configuration guide.

**RESEND\_API\_KEY** `string`

API key from your [Resend](https://resend.com) account. Required to send magic link login emails, client invitations, and password reset emails. If unset, Atrium displays a setup wizard on first launch.

**EMAIL\_FROM** `string` (default: [noreply@yourdomain.com](mailto:noreply@yourdomain.com))

The sender address shown on all outbound emails. Must be from a domain you have verified in your Resend account.

***

## Payments

These variables enable client invoice payments via Stripe. All are optional — leave them unset if you don't need Stripe payments.

For Stripe Connect mode you also need `STRIPE_MODE` and the corresponding secret key. See [Stripe Connect](/docs/payments/stripe-connect) for the full setup guide.

**STRIPE\_CONNECT\_CLIENT\_ID** `string`

Stripe Connect platform client ID (starts with `ca_`). Required for Stripe Connect mode. Enables the "Connect with Stripe" OAuth flow so clients can pay invoices directly.

**STRIPE\_CONNECT\_WEBHOOK\_SECRET** `string`

Webhook signing secret for Stripe Connect events (starts with `whsec_`). Required for Stripe Connect mode.

**STRIPE\_MODE** `string` (default: test)

Controls which Stripe environment to use. Accepted values: `test`, `live`. Used with Stripe Connect mode.

**STRIPE\_TEST\_SECRET\_KEY** `string`

Your platform Stripe secret key for test mode (starts with `sk_test_`). Used with Stripe Connect mode when `STRIPE_MODE=test`.

**STRIPE\_LIVE\_SECRET\_KEY** `string`

Your platform Stripe secret key for live mode (starts with `sk_live_`). Used with Stripe Connect mode when `STRIPE_MODE=live`.

**STRIPE\_CURRENCY** `string` (default: usd)

ISO 4217 currency code for invoice payments. Examples: `usd`, `eur`, `gbp`. Applies to all invoices across the organization.

***

## Security

**SECURE\_COOKIES** `boolean` (default: true)

Controls the `Secure` flag on session cookies. Defaults to `true` when `NODE_ENV=production`.

You should only set this to `false` if Atrium is accessed over plain HTTP with no TLS-terminating reverse proxy anywhere in the chain — for example, a local Unraid installation without HTTPS.

> [!WARNING]
>
> Setting `SECURE_COOKIES=false` allows session tokens to be transmitted over unencrypted connections. Using a reverse proxy with HTTPS (Caddy, nginx, Traefik) is strongly recommended instead of disabling secure cookies.

**FIREBASE\_HOSTING** `boolean` (default: false)

Enable only when deploying the web app behind Firebase Hosting. Applies a cookie workaround required by the Firebase CDN.

***

## Analytics

**NEXT\_PUBLIC\_TRACKERS** `string`

A JSON array of script tag attribute objects. Each object is serialized into a `<script>` tag in the web app's `<head>`. You can add any script-based analytics provider — Umami, Plausible, Fathom, and others are supported.

```bash theme={null}
NEXT_PUBLIC_TRACKERS='[{"src":"https://umami.example.com/script.js","data-website-id":"abc-123","defer":"true"}]'
```

Each key in the object becomes an attribute on the rendered `<script>` element. You can add multiple trackers by adding more objects to the array.

***

## Advanced

**LOG\_LEVEL** `string` (default: info)

Log verbosity level for the API, using Pino's log levels. Accepted values (least to most verbose): `fatal`, `error`, `warn`, `info`, `debug`, `trace`.

**THROTTLE\_LIMIT** `number` (default: 100)

Maximum number of API requests per minute, per IP address. Raising this limit is useful when running end-to-end tests locally to avoid cascading 429 errors.

**SIGNUP\_THROTTLE\_LIMIT** `number` (default: 5)

Maximum number of signup requests per minute, per IP address. Separate from the general `THROTTLE_LIMIT` to limit abuse of the registration endpoint.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.