---
title: 'Environment Variables'
description: 'Configure MCPHub using environment variables'
---

MCPHub reads the following environment variables at startup. The authoritative list is whatever appears under `process.env.*` in `src/`; this page summarizes everything currently read by the code on `main`.

## Core application

| Variable | Default | Description |
| --- | --- | --- |
| `PORT` | `3000` | HTTP server port. |
| `BASE_PATH` | `''` | Mount-point prefix when MCPHub is served under a sub-path (e.g. `/mcphub`). |
| `DEFAULT_REQUEST_TIMEOUT` | `60000` | Default request timeout (ms) for upstream MCP server requests when a server does not set `options.timeout`. |
| `INIT_TIMEOUT` | `300000` | Initial timeout (ms) used when starting upstream MCP servers. |
| `READONLY` | `false` | Reject mutating dashboard API requests. `GET` and `${basePath}/tools/*` remain allowed. |
| `DISABLE_WEB` | `false` | Skip serving the bundled dashboard. The API and MCP transport routes still run. |
| `TRUST_PROXY` | auto | Express `trust proxy` setting. Accepts `true`, `false`, a hop count (`1`, `2`, …), or names like `loopback,linklocal,uniquelocal`. Defaults to `1` inside Docker/Kubernetes, `false` otherwise. |
| `MCPHUB_SETTING_PATH` | `mcp_settings.json` | Override the path of the settings file used by file-mode storage. |
| `NODE_ENV` | `development` | `development` / `production` / `test`. |
| `DEBUG` | `false` | Set to `true` to enable verbose startup logging for frontend-path discovery. |

## Authentication

| Variable | Default | Description |
| --- | --- | --- |
| `ADMIN_PASSWORD` | random | Password seeded for the bootstrap `admin` user when no users exist yet. If unset, a 24-character random password is generated and printed to the logs. |
| `JWT_SECRET` | random per-process | Secret used to sign JWTs issued by `/api/auth/login`. See the warning below — this **must** be set explicitly in any non-development deployment. |

<Warning>
  **`JWT_SECRET` must be set to a persistent, high-entropy value (≥ 32 random bytes) in staging and production.** If it is left unset, MCPHub generates a new random secret on every process start, which means: every restart invalidates all outstanding sessions and bearer tokens; horizontally-scaled instances cannot share sessions because each replica signs with its own secret; and CI / container restarts silently log everyone out. Generate one with `openssl rand -hex 32` and store it in your secret manager. Treat it like any other long-lived cryptographic key — rotation requires invalidating outstanding JWTs.
</Warning>

### Better Auth (optional social login)

Better Auth is initialized once at process startup in `src/betterAuth.ts`. Because of that, changing any Better Auth setting still requires a restart.

For non-secret Better Auth settings, MCPHub resolves values in this order:

1. `BETTER_AUTH_*` environment variables
2. `systemConfig.auth.betterAuth` (from `mcp_settings.json` or the database-backed system config)
3. Built-in defaults

Provider client credentials remain environment-variable only.

<Note>
  Better Auth currently requires PostgreSQL-backed storage in MCPHub. In practice that means `DB_URL` must be configured; file-only mode does not support Better Auth sessions.
</Note>

| Variable | Description |
| --- | --- |
| `BETTER_AUTH_ENABLED` | Master switch for Better Auth. Better Auth is still disabled automatically when MCPHub is not in database mode or when no provider has the required credentials. |
| `BETTER_AUTH_URL` | Public base URL used to build Better Auth redirect URIs. This now takes precedence over `systemConfig.install.baseUrl`. Its origin is also auto-added to the trusted origin list. |
| `BETTER_AUTH_BASE_PATH` | Mount path for the Better Auth handler. Defaults to `/api/auth/better`. |
| `BETTER_AUTH_TRUSTED_ORIGINS` | Optional additional origins allowed to start Better Auth login flows. Accepts a comma-separated list, whitespace-separated list, or JSON array. When omitted, MCPHub still auto-trusts the origins from `BETTER_AUTH_URL` and `systemConfig.install.baseUrl`. |
| `BETTER_AUTH_GOOGLE_ENABLED` | Enable or disable the Google provider after credentials are present. Defaults to `true`. |
| `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` | Google OAuth app credentials. |
| `BETTER_AUTH_GITHUB_ENABLED` | Enable or disable the GitHub provider after credentials are present. Defaults to `true`. |
| `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` | GitHub OAuth app credentials. |
| `BETTER_AUTH_OIDC_ENABLED` | Enable or disable the generic OIDC provider. Defaults to `false`. |
| `BETTER_AUTH_OIDC_PROVIDER_ID` | Provider identifier passed to Better Auth's generic OAuth plugin. Defaults to `oidc`. |
| `BETTER_AUTH_OIDC_DISCOVERY_URL` | Discovery URL for a local OIDC issuer (for example Keycloak, Authentik, or Dex). |
| `OIDC_DISCOVERY_URL` | Legacy alias for `BETTER_AUTH_OIDC_DISCOVERY_URL`. It is also still useful for `${OIDC_DISCOVERY_URL}` interpolation inside `mcp_settings.json`. |
| `BETTER_AUTH_OIDC_SCOPES` | Optional requested scopes for the local OIDC provider. Accepts a comma-separated list, whitespace-separated list, or JSON array. Defaults to `openid profile email`. |
| `BETTER_AUTH_OIDC_PKCE` | Enable or disable PKCE for the local OIDC provider. Defaults to `true`. |
| `BETTER_AUTH_OIDC_PROMPT` | Optional `prompt` parameter for the local OIDC provider (`login`, `consent`, `select_account`, etc.). |
| `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` | Client credentials for a local OIDC provider such as Keycloak, Authentik, or Dex. |

You can still store the non-secret Better Auth settings in `systemConfig.auth.betterAuth`, but `BETTER_AUTH_*` variables now override those values at startup. A full env-only local OIDC example looks like this:

```env
BETTER_AUTH_ENABLED=true
BETTER_AUTH_URL=https://mcphub.example.com
BETTER_AUTH_BASE_PATH=/api/auth/better
BETTER_AUTH_TRUSTED_ORIGINS=https://dashboard.example.com,https://admin.example.com

BETTER_AUTH_OIDC_ENABLED=true
BETTER_AUTH_OIDC_PROVIDER_ID=local-oidc
BETTER_AUTH_OIDC_DISCOVERY_URL=https://auth.example.com/.well-known/openid-configuration
BETTER_AUTH_OIDC_SCOPES=openid,profile,email
BETTER_AUTH_OIDC_PKCE=true
BETTER_AUTH_OIDC_PROMPT=login
OIDC_CLIENT_ID=your-oidc-client-id
OIDC_CLIENT_SECRET=your-oidc-client-secret
```

## Storage

MCPHub picks file or database storage at boot:

```
useDatabase = (USE_DB === 'true') OR (USE_DB unset AND DB_URL is set)
```

| Variable | Default | Description |
| --- | --- | --- |
| `USE_DB` | unset | Force database mode (`true`) or file mode (`false`). |
| `DB_URL` | unset | PostgreSQL connection URL. Required for database mode. |
| `USE_DAO_LAYER` | unset | Force the DAO layer on even in file mode (advanced; usually unnecessary). |
| `DB_POOL_SIZE` | `10` | Maximum TypeORM connection pool size. |
| `DB_POOL_IDLE_TIMEOUT` | `30000` | Idle pool connection timeout (ms). |
| `DB_CONNECTION_TIMEOUT` | `30000` | Initial connect timeout (ms). |
| `DB_CONNECTION_RETRY_DELAY` | `2000` | Base delay between connection retries (ms). |
| `DB_MAX_CONNECTION_RETRIES` | `5` | Maximum connection retry attempts at startup. |
| `DB_ENABLE_HEALTH_CHECK` | `true` | Run periodic DB health probes (consumed by `/health`). |
| `DB_HEALTH_CHECK_INTERVAL` | `30000` | Health-probe interval (ms). |

See [Database Configuration](/configuration/database-configuration) for end-to-end setup.

## Smart Routing & embeddings

Smart Routing performs vector search over upstream tools to surface only the few most relevant tools for a given query. It requires Postgres + pgvector and an embedding endpoint.

| Variable | Default | Description |
| --- | --- | --- |
| `SMART_ROUTING_ENABLED` | `false` | Master switch for smart routing. |
| `SMART_ROUTING_EMBEDDING_PROVIDER` | `openai` | Embedding provider. Currently `openai` or `azure_openai`. |
| `SMART_ROUTING_EMBEDDING_ENCODING_FORMAT` | provider default | Override the `encoding_format` passed to the embedding API. |
| `SMART_ROUTING_BASE_PACING_DELAY_MS` | `0` | Base delay (ms) applied between embedding requests to avoid rate limits. |
| `SMART_ROUTING_PROGRESSIVE_DISCLOSURE` | unset | When set, expose progressively-disclosed tool metadata (see Smart Routing docs). |
| `EMBEDDING_MODEL` | `text-embedding-3-small` | Embedding model name. |
| `EMBEDDING_MAX_TOKENS` | per-model | Override token truncation limit before embedding. Useful to match a local inference server's batch size. |

### OpenAI

| Variable | Description |
| --- | --- |
| `OPENAI_API_KEY` | API key. |
| `OPENAI_API_BASE_URL` | Override the base URL (set this for any OpenAI-compatible endpoint, e.g. LocalAI). |

### Azure OpenAI

| Variable | Description |
| --- | --- |
| `AZURE_OPENAI_API_KEY` | API key. |
| `AZURE_OPENAI_ENDPOINT` | Resource endpoint (e.g. `https://my-resource.openai.azure.com`). |
| `AZURE_OPENAI_API_VERSION` | API version (e.g. `2024-02-01`). |
| `AZURE_OPENAI_EMBEDDING_DEPLOYMENT` | Deployment name. |
| `AZURE_OPENAI_EMBEDDING_MODEL` | The underlying OpenAI model the deployment maps to (e.g. `text-embedding-3-small`). Used for tokenizer and limit selection. |

## MCPRouter (optional upstream catalog)

| Variable | Description |
| --- | --- |
| `MCPROUTER_API_KEY` | API key for the MCPRouter cloud catalog. |
| `MCPROUTER_API_BASE` | Override the API base URL (default `https://api.mcprouter.to/v1`). |
| `MCPROUTER_REFERER` | Optional `Referer` header sent on MCPRouter requests. |
| `MCPROUTER_TITLE` | Optional `Title` header sent on MCPRouter requests. |

## Configuration examples

### Development

```env
NODE_ENV=development
PORT=3000
JWT_SECRET=dev-secret-key
```

### Production with database + Smart Routing

```env
NODE_ENV=production
PORT=3000
BASE_PATH=
JWT_SECRET=replace-with-32+-random-bytes

USE_DB=true
DB_URL=postgresql://mcphub:secret@db:5432/mcphub

SMART_ROUTING_ENABLED=true
OPENAI_API_KEY=sk-...
EMBEDDING_MODEL=text-embedding-3-small
```

## Loading order

MCPHub loads variables in this order; later sources override earlier ones:

1. System environment variables.
2. `.env.local` (gitignored).
3. `.env.{NODE_ENV}` (e.g. `.env.production`).
4. `.env`.

`dotenv-expand` is enabled, so `${VAR}` interpolation works inside `.env*` files.

## Security best practices

1. Never commit secrets — `.env*` files are gitignored by default; keep it that way.
2. Always set `JWT_SECRET` explicitly in production.
3. Rotate bearer keys (`/api/auth/keys`) and OAuth client secrets periodically.
4. Use Docker / Kubernetes secrets for container deployments instead of plain `-e`.
