Open WebUI with an OpenAI-compatible endpoint

Open WebUI is a self-hosted chat interface that teams put in front of models. It connects to OpenAI-compatible endpoints through environment variables, supports several endpoints at once, and — this is the part that catches everyone — stores those values as persistent config, so changing the environment later does nothing. This page covers all three, plus what a shared deployment costs per person.

In short: Open WebUI's OPENAI_API_BASE_URL and OPENAI_API_KEY are persistent config written to its database on first start, so changing the environment afterwards has no effect. The plural URL and key variables pair by position, not by name.

One endpoint: two environment variables

For a single endpoint set OPENAI_API_BASE_URL and OPENAI_API_KEY. The base URL ends in /v1; Open WebUI appends the rest of the path itself.

# docker-compose.yaml — one endpoint, values read from an .env file
# sitting next to this one (and kept out of git).
services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    ports:
      - "3000:8080"
    environment:
      - 'OPENAI_API_BASE_URL=${GATEWAY_BASE_URL}'   # https://aicomp.ai/v1
      - 'OPENAI_API_KEY=${GATEWAY_API_KEY}'
    volumes:
      - open-webui:/app/backend/data
volumes:
  open-webui:

Keeping the values in an .env file beside the compose file rather than inline means the compose file is safe to commit and the key is not. That matters more here than in a single-user tool, because these deployments usually end up in a shared repository.

Several endpoints: the lists pair by position

The plural variables, OPENAI_API_BASE_URLS and OPENAI_API_KEYS, are both semicolon-separated lists. They are matched by position — the first URL is used with the first key, the second with the second. There is no key-to-URL mapping beyond order, so a missing separator silently pairs every subsequent entry with the wrong key.

# Multiple endpoints: semicolon-separated, paired BY POSITION.
# The first URL uses the first key, the second URL the second key.
export OPENAI_API_BASE_URLS="https://aicomp.ai/v1;http://localhost:11434/v1"
export OPENAI_API_KEYS="sk-your-key;unused-for-local"

Local models typically ignore the key, but the slot still has to exist. A placeholder is fine; an empty slot is not, because it shifts the alignment for everything after it.

The trap: these values are persistent

OPENAI_API_BASE_URL and OPENAI_API_KEY are persistent config values. The first time Open WebUI starts, it writes them into its database. After that, changing the environment variable has no effect — the stored value wins. People restart containers for an hour wondering why the new endpoint is not being used.

To change an endpoint after the first run, change it in the admin connections UI, or reset the stored value. The environment variables are authoritative once, at first start.

Model list comes from the endpoint

The model dropdown is populated from /v1/models. An empty dropdown is a connection problem, not a missing-models problem — worth checking directly instead of reloading the interface.

# The model dropdown is populated from /v1/models. If it is empty,
# ask the endpoint directly rather than reloading the UI.
curl "$GATEWAY_BASE_URL/models" \
  -H "Authorization: Bearer $GATEWAY_API_KEY"

# Empty or 401 here means the UI can never populate either.

What a shared deployment costs

Self-hosting spreads one endpoint across a team, so the useful number is cost per person rather than cost per request. The table below assumes a five-person team at thirty chats each per working day, nine thousand input tokens and fourteen hundred output tokens per chat.

Cost of one chat at 9k input / 1.4k output, and of a five-person team over 20 working days. Rates checked 2026-09-20.
ModelVendorRate
in / out per 1M
Per chatTeam / month
gpt-5.6-lunaOpenAI$0.1 / $0.6$0.0017$5
MiniMax-M3MiniMax$0.15 / $0.6$0.0022$7
deepseek-v4-flashDeepSeek$0.22 / $0.66$0.0029$9
gemini-3.7-flashGoogle$0.375 / $1.875$0.0060$18
claude-haiku-4-5-20251001Anthropic$0.5 / $2.5$0.0080$24
deepseek-v4-proDeepSeek$0.66 / $1.98$0.0087$26
glm-5.3Zhipu$0.7 / $2.2$0.0094$28
qwen3.8-maxAlibaba$1 / $3$0.0132$40
claude-sonnet-5Anthropic$1 / $5$0.0160$48
gpt-5.6-terraOpenAI$1 / $6$0.0174$52
kimi-k3Moonshot$1.5 / $7.5$0.0240$72
claude-opus-5Anthropic$2.5 / $12.5$0.0400$120
Cost note. A shared interface changes which rate matters. Chat traffic is output-heavy — the model writes far more than the user does — so the output rate dominates, and the gap between models compounds across every person using the deployment. This is also why a per-user default model is worth setting rather than letting everyone use the strongest one.

How this fails in practice

The failures that account for most setup problems, and what each one actually means.
What you seeWhat it usually isFix
Changing the env var does nothingThese are persistent config values written to the database on first startChange the endpoint in the admin connections UI, or reset the stored value
Wrong key used with a URLURL and key lists are paired by position; a separator is missing or extraCount the semicolons in both variables — they must produce the same number of entries
Model dropdown stays emptyEndpoint unreachable, or /v1/models rejects the keyQuery /v1/models directly with the same key
404 from the endpointBase URL missing /v1Set OPENAI_API_BASE_URL to a URL ending in /v1
Container cannot reach a host servicelocalhost inside the container is the container, not the hostUse host.docker.internal on Docker Desktop, or the host gateway address on Linux
Key committed to the repositoryValues written inline in a committed compose fileKeep them in .env and reference by name; add .env to .gitignore
You need a key before the code below runs. Create an account, generate a key, and copy the base URL (https://aicomp.ai/v1). Create one free →
Check current rates → Free to sign up · $1 minimum top-up · No prepayment

FAQ

Why does changing the environment variable not change the endpoint?

Because OPENAI_API_BASE_URL and OPENAI_API_KEY are persistent config. Open WebUI writes them to its database on first start and reads from there afterwards, so the environment is only consulted once.

How do I connect more than one endpoint?

Use OPENAI_API_BASE_URLS and OPENAI_API_KEYS, both semicolon-separated. They pair by position, so the first URL uses the first key and so on — keep the two lists the same length.

Does the base URL need /v1?

Yes. Open WebUI appends the rest of the request path itself, so the base URL you provide ends in /v1.

Why is my model list empty?

The list is fetched from /v1/models. If that call fails the dropdown is empty — which is a connection or credentials problem rather than a missing model. Query the endpoint directly to confirm.

Can Open WebUI reach a model running on the same machine?

Yes, but not via localhost from inside a container — that resolves to the container. Use host.docker.internal on Docker Desktop, or the host gateway address on Linux.

How do I keep the API key out of version control?

Put it in an .env file beside the compose file, reference it by name, and add .env to .gitignore. The compose file itself is then safe to commit.

Related

Get API access