Every environment variable the Open branch reads and where to set it, plus the access control, reverse proxy and HTTPS an exposed instance needs.
You run the Open branch. Everything here applies to the open-source main branch on a machine you control: the same product the hosted site runs, with the pieces hosted operates for you left in your hands. The code lives on GitHub - if Open serves you, star the repo.
Every variable is optional. The app starts with an empty environment, and each person configures their own AI services inside it. Each variable below adds a capability or moves a default. One AI service key is the whole minimum for an instance that works on first load.
a person's own setting -> your environment variable -> the built-in default
That order holds at every point where a credential or an address is read. One deliberate inversion: when a Bedrock request falls through to your server credential, the region comes from BEDROCK_REGION. The region the browser sent is ignored.
Set the key and the service appears, already configured, for everyone who opens the instance.
| Variable | What it does | Default |
|---|---|---|
ANTHROPIC_API_KEY · ANTHROPIC_API_HOST | Anthropic credential; host override | api.anthropic.com |
OPENAI_API_KEY · OPENAI_API_HOST · OPENAI_API_ORG_ID | OpenAI credential; host override; organization id | api.openai.com |
GEMINI_API_KEY | Google Gemini credential | - |
DEEPSEEK_API_KEY · GROQ_API_KEY · MISTRAL_API_KEY · MOONSHOT_API_KEY · OPENROUTER_API_KEY · PERPLEXITY_API_KEY · TOGETHERAI_API_KEY · XAI_API_KEY | One credential each | - |
ALIBABA_API_KEY · ALIBABA_API_HOST | Alibaba credential; host override | https://dashscope-intl.aliyuncs.com/compatible-mode |
NVIDIANIM_API_KEY · NVIDIANIM_API_HOST | NVIDIA NIM credential; host override | https://integrate.api.nvidia.com |
CEREBRAS_API_KEY | Cerebras credential - reports nothing | - |
SAKANA_API_KEY · SAKANA_API_HOST | Sakana credential; host override; reports nothing too | https://api.sakana.ai |
OLLAMA_API_HOST | Address of an Ollama server the app's server can reach | http://127.0.0.1:11434 |
LOCALAI_API_HOST · LOCALAI_API_KEY | Address of a LocalAI server; optional credential | http://127.0.0.1:8080 |
AZURE_OPENAI_API_ENDPOINT · AZURE_OPENAI_API_KEY | Azure endpoint and credential - both required together | - |
AZURE_OPENAI_API_VERSION · AZURE_DEPLOYMENTS_API_VERSION · AZURE_OPENAI_DISABLE_V1 | Azure API versions; true turns the v1 API off | 2025-04-01-preview · 2023-03-15-preview · unset |
BEDROCK_BEARER_TOKEN or BEDROCK_ACCESS_KEY_ID + BEDROCK_SECRET_ACCESS_KEY | AWS Bedrock credential, either form | - |
BEDROCK_SESSION_TOKEN · BEDROCK_REGION | Temporary-credential token; region, server-side only | - · us-east-1 |
Azure's v1 API is the default. It calls /openai/v1/responses with no deployment id; disabling it sends every model back through /openai/deployments/<name>/.
Cohere, LM Studio and Z.ai take no server-side variable: those are added per person in the app.
| Variable | What it does | Default |
|---|---|---|
PUPPETEER_WSS_ENDPOINT | Remote browser for page fetching. ws:// or wss:// only | - |
GOOGLE_CLOUD_API_KEY · GOOGLE_CSE_ID | Custom Search credentials - both required, or neither works | - |
ELEVENLABS_API_KEY · ELEVENLABS_API_HOST · ELEVENLABS_VOICE_ID | ElevenLabs credential, host, voice - unread today | - |
POSTGRES_PRISMA_URL · POSTGRES_URL_NON_POOLING | Share-link database - both required | - |
| Variable | What it does | Default |
|---|---|---|
HTTP_BASIC_AUTH_USERNAME · HTTP_BASIC_AUTH_PASSWORD | Credentials for the basic-auth middleware, which also has to be enabled in a build - below | - |
AIX_STRICT_PARSING | true makes the wire parser throw in production instead of warning. For diagnosing an AI service whose API drifted | warn only |
These are read when the JavaScript is built, so they take effect only in a build you run.
| Variable | What it does | Default |
|---|---|---|
NEXT_PUBLIC_MOTD | The message-of-the-day banner - below | - |
NEXT_PUBLIC_GOOGLE_DRIVE_CLIENT_ID | Google Drive in the attachment menu | - |
NEXT_PUBLIC_PLANTUML_SERVER_URL | Where diagrams are rendered | https://www.plantuml.com/plantuml/svg/ |
NEXT_PUBLIC_GA4_MEASUREMENT_ID · NEXT_PUBLIC_POSTHOG_KEY | Your own analytics properties - below | - |
NEXT_PUBLIC_BUILD_HASH | Overrides the build identifier the app displays | git revision, else 2-dev |
NEXT_PUBLIC_DEBUG_BREAKS | true pauses the browser debugger on every error, critical and DEV log. Development builds only: npm run dev, never a published image | - |
Three rules apply to all of the above:
api.openai.com fails the build; https://api.openai.com passes.| The field takes | AI services |
|---|---|
| several keys | Alibaba · Cerebras · Cohere · Deepseek · Gemini · Groq · Mistral · Moonshot · NVIDIA NIM · OpenRouter · Perplexity · Sakana · Together AI · xAI · Z.ai |
| one value | OpenAI · Anthropic · Azure · Bedrock · LocalAI · Ollama |
Everything named NEXT_PUBLIC_* is compiled into the JavaScript when the app is built. Everything else is read from the process environment while it runs.
| What you do | What happens |
|---|---|
prebuilt image + -e OPENAI_API_KEY=... | works immediately, no rebuild |
prebuilt image + -e NEXT_PUBLIC_MOTD=... | nothing happens, ever |
| a server-side variable changed | takes effect on restart |
its name contains _API_ | every browser re-scans its model services once |
The message of the day, Drive import, the analytics keys and the diagram server need a build you control: a Vercel project variable, or a build from source. The re-scan is Update.
| You run it with | Put them in |
|---|---|
| Source build | .env in the project root |
docker run | --env-file .env, or one -e per variable |
| Compose | the env_file entry the shipped file already has |
| Kubernetes | stringData in the secret manifest |
| Vercel | the project's Environment Variables screen |
A variable that was accepted and changes nothing is one of the cases above, or a name that never reached the process. Everything else is My self-hosted instance is broken.
/dev/debug on your instance lists what the server picked up as capability booleans - this provider configured, that one not - plus build information, and never a value. Download debug JSON captures the same for a report. A variable that reads false there never reached the process: the name, the file, or the restart is missing. A variable that reads true and still misbehaves is a request-time problem: My self-hosted instance is broken.
A key in your server's environment belongs to the instance: everyone who opens it has working models and nobody types a secret. A key someone pastes into the app belongs to that browser alone. Both work at once, decided per service - and the person always wins: a key typed into the UI takes precedence over yours for that browser. Either way, whoever can reach the instance decides who spends, so access control goes in front of the app. The open branch ships no sign-in of its own and sits behind whatever authentication you already run.
| Where | What a person sees |
|---|---|
| first load | the service creates itself, fetches its models, picks one per job |
| Models -> Add service | a lime mark on the provider's icon |
| the setup panel | API key field: Already set on server, with a green check - the field stops being required |
Nobody opens Models. The three browser-only vendors above - Cohere, LM Studio, Z.ai - still ask each person for a key, and Cohere's error names the difference: Missing Cohere API Key. Add it on the UI (Models Setup).
Not the value. The browser is sent booleans - this provider is configured, that one is not - plus a hash and build information. No variable's value is ever serialised to the client. Provider-side, requests arrive from your server's address on your account: the dashboard attributes them to you, not to whoever asked.
The spend, entirely. In the open branch every API procedure is unauthenticated. Anyone who can reach the instance's API can bill your key without opening the app. Everything below is the fix.
| Anyone who has the URL can | What it costs you |
|---|---|
| Send model requests through the API | Every key you set server-side, without limit |
| List which providers and services you configured | Booleans and your build hash, at /dev/debug |
| Run a web search | Your search quota |
| Synthesise speech | Your OpenAI key, through the speech route |
| Fetch a transcript | Your server's bandwidth |
| Make your server fetch any URL | Your remote-browser endpoint, unauthenticated by default |
| Write and read share links | Rows in your database |
The app's dev pages - /dev, /dev/debug, /dev/llms, /dev/inspect/* - are served in production: /dev/debug lists what the server picked up, and /dev/llms probes your configured models on a click.
The strongest options need no rebuild and cover every path, dev pages included. Each is its vendor's own procedure:
auth_basic in nginx, basicauth in Caddy, forward-auth in Traefik.Company SSO belongs in the same layer. An identity-aware proxy - oauth2-proxy, Authelia, Pomerium, Cloudflare Access - authenticates the person and passes an already-authorised request through. Big-AGI needs no integration for this, and has none.
The repository ships middleware_BASIC_AUTH.ts. Rename it to middleware.ts and rebuild, then set HTTP_BASIC_AUTH_USERNAME and HTTP_BASIC_AUTH_PASSWORD. It is a build-time change, so the prebuilt image cannot have it.
| Basic auth | Paths |
|---|---|
| covered | /, everything under /api, a handful of app routes |
| not covered | /workspace, /draw, /diff, /tokens, /dev/* |
Your keys are covered; the dev pages are not. To cover everything with one control, use the platform or proxy option above.
Two response bodies tell you which mistake you made:
| Response body | What it means |
|---|---|
Unauthorized | no credentials, or wrong ones |
Unauthorized/Unconfigured | middleware enabled, the two variables are not set |
The second also logs HTTP Basic Authentication is enabled but not configured on the server: the file was renamed and the variables were never set.
/dev/*, which lists your configuration.Streaming answers arrive as one long chunked application/json response on /api/* - not server-sent events. Four requirements follow:
/api/*.Upgrade and Connection passed through, if you also want the in-app developer tooling.location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
client_max_body_size 100M;
}
Caddy and Traefik do not buffer by default; their timeout and body-size settings still need raising. Answers that appear all at once at the end, die partway, or bounce with **Request too large** are the requirements above being violated - the symptom-by-symptom decode is Stalled or cut off.
Caddy and Traefik obtain certificates by themselves; nginx and Apache use certbot - their documentation, not ours. The Big-AGI-specific reason it matters beyond secrecy: microphone capture, clipboard reads and screen capture are secure-context browser features. http://localhost counts as secure; a LAN address like http://192.168.1.10 does not. On a development machine, npm run dev-https serves the dev server over TLS.
NEXT_PUBLIC_MOTD renders a banner above the app for everyone on the instance. Build-time only, like every NEXT_PUBLIC_* variable, and each person can dismiss it permanently. Two placeholders expand at render: {{app_build_hash}} and {{app_build_pkgver}}.
Nothing, by default. The two analytics integrations activate only when a build carries your own keys - NEXT_PUBLIC_GA4_MEASUREMENT_ID, NEXT_PUBLIC_POSTHOG_KEY - and then report to your properties, not ours. The shipped Dockerfile sets NEXT_TELEMETRY_DISABLED=1, so Next.js's own telemetry is off in every image built from it.
| What happens | How to tell |
|---|---|
| Someone types their own key into a service you configured | They spend their own from that moment - there is no per-person quota and no way to force yours |
| Cerebras and Sakana answer requests and announce nothing | No lime mark, no green check, no self-configuration - add the service by hand, key field empty |
The Already set on server label goes as soon as someone types a custom OpenAI host | Your server key is not used against an address the browser chose |
Basic auth is on and /dev/* still answers | Load /dev/debug from outside your session; a stranger reads the same capability list |
| The microphone stops working on a LAN address over plain HTTP | Secure-context features need HTTPS or localhost, and nothing in the app says so |
A key that is set but never reaches the request says so per provider, in one shape: Missing OpenAI API Key. Add it on the UI or server side (your deployment). Confirm the server read it at all before touching anything else: Check what the server read. Errors the provider sent back are access errors from the provider. Streaming that stalls without a proxy in the path is Stalled or cut off. Anything else on your own deployment is My self-hosted instance is broken.
BIG-AGI
Resources
© 2026 Token Fabrics·Built with passion in San Diego