> ## Documentation Index
> Fetch the complete documentation index at: https://manifest.build/llm-gateway/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Environment variables

> Environment variables for a self-hosted Manifest LLM Gateway.

The gateway reads its configuration from environment variables. In the bundled Docker setup these come from `~/manifest/.env`. For `docker run`, pass them with `-e`. For non-Docker installs, export them in the shell before launching the backend.

<Note>
  This page is for [self-hosted](/llm-gateway/docs/llm-gateway/docs/self-hosted) instances only. On [LLM Gateway
  Cloud](https://app.manifest.build) there is no server to configure and no
  `.env` to edit — everything you can change lives in the dashboard.
</Note>

## Core

| Variable             | Required | Default                 | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| -------------------- | -------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DATABASE_URL`       | Yes      | —                       | PostgreSQL connection string. Format: `postgresql://user:pass@host:5432/dbname`                                                                                                                                                                                                                                                                                                                                                                                                 |
| `BETTER_AUTH_SECRET` | Yes      | —                       | Session signing secret. Min 32 chars. Generate with `openssl rand -hex 32`                                                                                                                                                                                                                                                                                                                                                                                                      |
| `BETTER_AUTH_URL`    | No       | `http://localhost:2099` | Public URL the dashboard is reachable on. Must match the browser URL                                                                                                                                                                                                                                                                                                                                                                                                            |
| `PORT`               | No       | `2099`                  | Dashboard port. Under the bundled compose file this sets both the published host port and the internal listener, and `BETTER_AUTH_URL` follows it                                                                                                                                                                                                                                                                                                                               |
| `NODE_ENV`           | No       | `production`            | Node environment. Telemetry is disabled when this isn't `production`. Fixed to `production` by the bundled compose file — the image is a production artifact                                                                                                                                                                                                                                                                                                                    |
| `MANIFEST_MODE`      | No       | —                       | Marks the install as self-hosted when the gateway can't tell on its own. Accepted value: `selfhosted` (`local` is a legacy alias with the same effect). Unset, the gateway detects Docker, Podman, and Kubernetes automatically; some platforms hide the container, which is why the [Render](/llm-gateway/docs/llm-gateway/docs/deploy/render), [Koyeb](/llm-gateway/docs/llm-gateway/docs/deploy/koyeb), [Easypanel](/llm-gateway/docs/llm-gateway/docs/deploy/easypanel), and [AWS](/llm-gateway/docs/llm-gateway/docs/deploy/aws) guides set it |

## Network & security

| Variable                  | Default                 | Description                                                                                                                                                                                                                                         |
| ------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BIND_ADDRESS`            | `127.0.0.1`             | Interface the server binds to. Set to `0.0.0.0` for LAN access. The Docker image already sets `0.0.0.0`; under the bundled compose file, control host exposure with `HOST_BIND_ADDRESS` instead                                                     |
| `HOST_BIND_ADDRESS`       | `127.0.0.1`             | Host interface the bundled compose file binds the dashboard port to. Set `0.0.0.0` to expose it on the LAN                                                                                                                                          |
| `CORS_ORIGIN`             | `http://localhost:3000` | Allowed CORS origin for browser-based dashboard requests. Development only — production uses the built-in allowlist plus `WINGMAN_CORS_ORIGINS`                                                                                                     |
| `API_KEY`                 | —                       | Internal API key for system endpoints                                                                                                                                                                                                               |
| `MANIFEST_ENCRYPTION_KEY` | `BETTER_AUTH_SECRET`    | Key used to encrypt stored provider credentials at rest. Falls back to `BETTER_AUTH_SECRET` when unset — set a separate 32+ char value so a session-cookie leak doesn't also decrypt every stored provider key. Set it before first boot            |
| `MANIFEST_DISABLE_HSTS`   | —                       | Set `1` to silence the boot warning about HSTS being unavailable on a plain-HTTP deployment. Prefer an `https://` `BETTER_AUTH_URL` anywhere reachable from the internet                                                                            |
| `WINGMAN_CORS_ORIGINS`    | —                       | Extra browser origins allowed to call the gateway, comma-separated. Useful when you host your own copy of [Wingman](https://github.com/mnfst/wingman), the open-source gateway tester; the hosted one at `wingman.manifest.build` is always allowed |
| `THROTTLE_TTL`            | `60000`                 | Window (ms) for the rate limit on the dashboard's own API calls. Does not apply to gateway traffic on `/v1`                                                                                                                                         |
| `THROTTLE_LIMIT`          | `100`                   | Max dashboard API calls per window. The gateway has its own fixed caps, see [Rate limits](/llm-gateway/docs/llm-gateway/docs/reference/api#rate-limits)                                                                                                              |

## Database

| Variable                 | Default | Description                                                                                                                                                                                                    |
| ------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DB_POOL_MAX`            | `10`    | Max PostgreSQL connections in the main pool                                                                                                                                                                    |
| `AUTH_DB_POOL_MAX`       | `5`     | Separate pool used by Better Auth. Counted on top of `DB_POOL_MAX` when sizing your server's `max_connections`                                                                                                 |
| `RUN_MIGRATIONS_ON_BOOT` | `true`  | Run pending migrations at startup. Set `false` on multi-replica deploys so only one instance migrates                                                                                                          |
| `DB_TUNE_SESSION`        | `true`  | Apply the gateway's planner defaults at boot. Set `false` on managed Postgres where your role can't `ALTER ROLE` itself                                                                                        |
| `SEED_DATA`              | `false` | Seed demo data on first boot. Development only — the seeder refuses to run under `NODE_ENV=production`, so it has no effect on a Docker self-host. Use the first-run setup wizard to create your admin account |

## LLM proxy

How the gateway talks to upstream providers.

| Variable              | Default                  | Description                                                                                                                                                                                                                                                   |
| --------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PROVIDER_TIMEOUT_MS` | `180000`                 | Per-attempt timeout (ms) for upstream requests. A hung provider surfaces as a synthetic `504` and triggers the next [fallback](/llm-gateway/docs/llm-gateway/docs/llm-gateway#fallback). Set it strictly below your client's own timeout; slow local models may need it raised |
| `STREAM_WARMUP_MS`    | `15000`                  | How long to wait for the first streamed chunk before falling back to the next model                                                                                                                                                                           |
| `OLLAMA_HOST`         | `http://localhost:11434` | Base URL for a local Ollama server. In Docker, set it to `http://host.docker.internal:11434`                                                                                                                                                                  |

## Request recordings

Storage and retention for [request logs](/llm-gateway/docs/llm-gateway/docs/request-logs). The bundled Docker setup already sets the filesystem path and mounts a volume for it, so a default install needs none of these.

| Variable                                 | Default                    | Description                                                                                                                          |
| ---------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `REQUEST_RECORDING_STORAGE`              | `auto`                     | `auto`, `s3`, `filesystem`, or `disabled`. Under `auto`, any S3 variable being set selects S3; otherwise the filesystem path is used |
| `REQUEST_RECORDING_FILESYSTEM_PATH`      | `/data/request-recordings` | Where recordings are written on disk. Must be a persistent volume                                                                    |
| `REQUEST_RECORDING_RETENTION_DAYS`       | `365`                      | Days to keep recordings before the sweep deletes them                                                                                |
| `REQUEST_RECORDING_S3_BUCKET`            | —                          | Bucket name. Required for S3                                                                                                         |
| `REQUEST_RECORDING_S3_REGION`            | —                          | Region. Use `auto` for R2 and similar. Required for S3                                                                               |
| `REQUEST_RECORDING_S3_ENDPOINT`          | —                          | Custom endpoint for non-AWS S3-compatible storage                                                                                    |
| `REQUEST_RECORDING_S3_ACCESS_KEY_ID`     | —                          | Access key. Omit both key variables to use ambient credentials                                                                       |
| `REQUEST_RECORDING_S3_SECRET_ACCESS_KEY` | —                          | Secret key. Must be set together with the access key ID                                                                              |
| `REQUEST_RECORDING_S3_FORCE_PATH_STYLE`  | `false`                    | Set `true` for storage that needs path-style bucket addressing (MinIO)                                                               |

<Warning>
  A partial S3 configuration disables recording rather than falling back to
  local disk. Set bucket **and** region, and either both access-key variables or
  neither.
</Warning>

## Autofix

[Autofix](/llm-gateway/docs/llm-gateway/docs/autofix) works with zero configuration; `AUTOFIX_GLOBAL_ENABLED=false` is the kill switch.

| Variable                      | Default       | Description                                                                                                                                                                                                                                         |
| ----------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AUTOFIX_GLOBAL_ENABLED`      | `true`        | Set `false` to turn Autofix off for the whole deployment. No call reaches the healing service, including the boot health check                                                                                                                      |
| `AUTOFIX_TIMEOUT_MS`          | `10000`       | Timeout per healing call, in milliseconds                                                                                                                                                                                                           |
| `AUTOFIX_REPAIRABLE_STATUSES` | `400,404,422` | Which provider statuses are eligible for a repair                                                                                                                                                                                                   |
| `AUTOFIX_HEALING_API_KEY`     | unset         | Sent as `x-api-key` to the healing service. Self-hosted installs don't need it; with no key set, the install announces its anonymous install id instead. Only relevant when provided by the hosted healing service                                  |
| `AUTOFIX_REPORT_ALL_4XX`      | `false`       | Opt-in. Also report request-side 4xx errors (except `401`/`402`/`403`/`429`) from harnesses with Autofix on, as diagnostic evidence with the secret-scrubbed request body. Bodies over 256 KB are dropped, not truncated. No fix comes back from it |

## Email

Used for both Better Auth transactional emails (signup verification, password reset) **and** [threshold alerts](/llm-gateway/docs/llm-gateway/docs/observability#spend-alerts). Set one provider block.

A provider saved on a harness's [Limits page](/llm-gateway/docs/llm-gateway/docs/observability#email-delivery) takes precedence for threshold alerts. These variables stay the only path for verification and password-reset email.

| Variable         | Description                        |
| ---------------- | ---------------------------------- |
| `EMAIL_PROVIDER` | `resend`, `mailgun`, or `sendgrid` |
| `EMAIL_API_KEY`  | API key for the chosen provider    |
| `EMAIL_DOMAIN`   | Sending domain (Mailgun only)      |
| `EMAIL_FROM`     | From-address for outbound mail     |

<Note>
  Without an email provider, signup verification is waived (users are created as unverified-but-usable) and password reset silently no-ops. Hard-limit blocks still work, and alerts still email if a provider is saved on a harness's **Limits** page.
</Note>

### Legacy Mailgun-only

Older deployments used these. Kept for backward compatibility; new installs should use `EMAIL_*` instead.

| Variable                  | Description               |
| ------------------------- | ------------------------- |
| `MAILGUN_API_KEY`         | Mailgun API key           |
| `MAILGUN_DOMAIN`          | Mailgun domain            |
| `NOTIFICATION_FROM_EMAIL` | Sender address for alerts |

## OAuth logins

Each provider activates automatically when both `*_CLIENT_ID` and `*_CLIENT_SECRET` are set. Configure the callback URL in the provider's console as `${BETTER_AUTH_URL}/api/auth/callback/<provider>`.

| Variable                | Provider                    |
| ----------------------- | --------------------------- |
| `GOOGLE_CLIENT_ID`      | Google OAuth client ID      |
| `GOOGLE_CLIENT_SECRET`  | Google OAuth client secret  |
| `GITHUB_CLIENT_ID`      | GitHub OAuth client ID      |
| `GITHUB_CLIENT_SECRET`  | GitHub OAuth client secret  |
| `DISCORD_CLIENT_ID`     | Discord OAuth client ID     |
| `DISCORD_CLIENT_SECRET` | Discord OAuth client secret |

Subscription OAuth (ChatGPT, Claude, MiniMax) uses gateway-provided client IDs by default. Override only if you've registered your own app:

| Variable                  | Description                    |
| ------------------------- | ------------------------------ |
| `OPENAI_OAUTH_CLIENT_ID`  | Custom OpenAI OAuth client ID  |
| `MINIMAX_OAUTH_CLIENT_ID` | Custom MiniMax OAuth client ID |

## Telemetry

| Variable                      | Default                                | Description                                                                        |
| ----------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------- |
| `MANIFEST_TELEMETRY_DISABLED` | unset                                  | Set to `1` to disable [anonymous telemetry](/llm-gateway/docs/llm-gateway/docs/reference/telemetry) |
| `TELEMETRY_ENDPOINT`          | `https://telemetry.manifest.build/...` | Send reports to your own endpoint                                                  |
| `MANIFEST_PUBLIC_STATS`       | `false`                                | Set `true` to expose `/api/v1/public/*` aggregate stats without auth               |

## Error monitoring

Sentry is opt-in and stays off unless you give it a DSN.

| Variable             | Default    | Description                                                        |
| -------------------- | ---------- | ------------------------------------------------------------------ |
| `SENTRY_DSN`         | unset      | Your Sentry project DSN. Leave it unset to disable Sentry entirely |
| `SENTRY_ENVIRONMENT` | `NODE_ENV` | Environment tag on reported events                                 |
| `SENTRY_RELEASE`     | unset      | Release tag on reported events                                     |

## Operations

| Variable            | Default | Description                                                                                                                                                                  |
| ------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SHUTDOWN_DRAIN_MS` | `10000` | How long the server keeps serving traffic after a termination signal while reporting unhealthy, so a load balancer can deregister it first. Set `0` to shut down immediately |

## Image version

| Variable           | Default  | Description                                                                                                                                                                                                                                            |
| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MANIFEST_VERSION` | `latest` | Image tag the bundled compose file pulls. Set `6` to follow a major line, or `6.18.0` to pin one release. Docker Compose substitutes it into the image name; the app itself never reads it. See [Image tags](/llm-gateway/docs/llm-gateway/docs/self-hosted#image-tags) |

## Postgres bundled volume

When you run the bundled compose file, the gateway brings up its own Postgres container. To use a stronger password than the default, set **both** of these. They must agree, and special characters in the password must be percent-encoded in `DATABASE_URL`:

| Variable            | Description                                  |
| ------------------- | -------------------------------------------- |
| `POSTGRES_PASSWORD` | Password for the bundled Postgres user       |
| `DATABASE_URL`      | Connection string with the matching password |

Special-char encoding: `@` → `%40`, `:` → `%3A`, `/` → `%2F`.
