Goal
You already run a model on vLLM or Ollama, and people want a chat UI for it. Open WebUI is the usual choice. If you run it with the defaults it works, but you get an open signup page and a few surprises when you change settings later. This guide sets it up so that:
- it talks to your model server over the internal network only,
- nobody can create an account without approval (or SSO),
- the session signing key stays the same when the container is recreated,
- the data volume is backed up and the service has a health check.
1. Check the model server first
Open WebUI only shows what the backend returns from its models endpoint. Test that before you touch the UI.
vLLM (OpenAI-compatible, started with --api-key):
curl -s http://vllm.internal:8000/v1/models -H "Authorization: Bearer $VLLM_API_KEY" | jq '.data[].id'
Ollama:
curl -s http://ollama.internal:11434/api/tags | jq '.models[].name'
If those calls fail, fix them first. Open WebUI will just show an empty model list.
2. Run Open WebUI with Docker Compose
services:
open-webui:
image: ghcr.io/open-webui/open-webui:main # pin a release tag in production
container_name: open-webui
restart: always
ports:
- "127.0.0.1:3000:8080" # only the reverse proxy reaches it
volumes:
- open-webui:/app/backend/data
environment:
WEBUI_URL: "https://chat.internal.example.com"
WEBUI_SECRET_KEY: "${WEBUI_SECRET_KEY}"
# vLLM as an OpenAI-compatible backend
ENABLE_OPENAI_API: "true"
OPENAI_API_BASE_URL: "http://vllm.internal:8000/v1"
OPENAI_API_KEY: "${VLLM_API_KEY}"
# Turn Ollama off if you don't use it (or set OLLAMA_BASE_URL instead)
ENABLE_OLLAMA_API: "false"
# Accounts
ENABLE_SIGNUP: "false"
DEFAULT_USER_ROLE: "pending"
ENABLE_COMMUNITY_SHARING: "false"
volumes:
open-webui:
Generate the secret once and store it in your secret store, not in the compose file:
openssl rand -hex 32
For Ollama, swap the OpenAI block for:
ENABLE_OLLAMA_API: "true"
OLLAMA_BASE_URL: "http://ollama.internal:11434"
ENABLE_OPENAI_API: "false"
For several backends, OPENAI_API_BASE_URLS and OPENAI_API_KEYS (and OLLAMA_BASE_URLS) take semicolon-separated lists in matching order.
Warning
WEBUI_SECRET_KEYsigns login tokens and is used to encrypt stored data. Docker generates a random one on first start if you don't set it. If it changes, for example because the container is recreated somewhere it can't find the old key, everyone is logged out and encrypted settings may stop working. Set it yourself and keep it the same.
Create the admin account straight away
The first account registered becomes the admin. Start the container, open the URL right away and register the admin account yourself before you announce the service. Leave the port bound to 127.0.0.1 (as above) until you have done this.
3. Know which settings the environment controls
This catches most people out. Many Open WebUI settings are persistent config: on first start the environment value is written to the database, and from then on the database value wins. If you change ENABLE_SIGNUP or OPENAI_API_BASE_URL in compose later and restart, nothing happens.
| You changed | It didn't apply because | Fix |
|---|---|---|
ENABLE_SIGNUP, DEFAULT_USER_ROLE | Value already stored in the database | Change it in Admin Panel > Settings, or set ENABLE_PERSISTENT_CONFIG=false |
OPENAI_API_BASE_URL, API keys | Connection stored in the database | Edit it under Admin Panel > Settings > Connections |
OLLAMA_BASE_URL | Not persistent, but the OLLAMA_BASE_URLS list may be | Check Connections in the admin panel |
WEBUI_AUTH=false | Only allowed on a fresh install with no users | Leave auth on. Don't run an internal UI without it |
ENABLE_PERSISTENT_CONFIG=false makes the environment the source of truth on every start. That suits GitOps-managed deployments. Settings edited in the UI are then lost on restart, so pick one approach and write it down in the runbook.
4. Add SSO instead of local passwords
With OIDC (Entra ID, Okta, Keycloak and others), people sign in with their corporate account and leavers lose access when their account is disabled.
ENABLE_OAUTH_SIGNUP: "true"
OAUTH_PROVIDER_NAME: "Corporate SSO"
OPENID_PROVIDER_URL: "https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration"
OAUTH_CLIENT_ID: "${OIDC_CLIENT_ID}"
OAUTH_CLIENT_SECRET: "${OIDC_CLIENT_SECRET}"
ENABLE_LOGIN_FORM: "false" # after you have confirmed an SSO admin can sign in
Register the redirect URI with your identity provider. Its path is in the Open WebUI SSO docs for your version, so check it there. WEBUI_URL must be set to the public HTTPS address before OAuth will work.
Caution
Don't turn off
ENABLE_LOGIN_FORMuntil an SSO account has signed in and has the admin role. Otherwise you can lock yourself out of the admin panel.
ENABLE_OAUTH_SIGNUP=true creates accounts on first SSO login. Together with DEFAULT_USER_ROLE=pending, an admin still has to approve each new account. To open it to everyone in the tenant, restrict access in the identity provider (assigned users only) and set the default role to user.
5. Control who sees which models
- Keep
BYPASS_MODEL_ACCESS_CONTROLat its default (false). When it istrue, every user can use every model. - Set model visibility and group access in the admin panel. The menu names have changed between releases, so look under Admin Panel > Settings > Models or Workspace > Models.
- API keys for users are off by default (
ENABLE_API_KEYS=false). Leave them off unless you need them. Otherwise the UI becomes an unmetered side door to your GPU.
6. Reverse proxy, streaming and health checks
Open WebUI uses WebSockets and streams tokens, so the proxy must pass both through:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 300s;
}
Health check for the load balancer or monitoring. It needs no login and returns 200 when the app and its database respond:
curl -fsS http://127.0.0.1:3000/health
/health doesn't tell you whether the model backend is reachable. For that, monitor the vLLM or Ollama endpoint directly with the calls from step 1.
7. Back up the data volume
Users, chats, settings and uploaded files live in /app/backend/data (SQLite by default). Back it up while the container is stopped, or during a quiet window:
docker stop open-webui
docker run --rm -v open-webui:/data -v "$PWD":/backup alpine \
tar czf /backup/open-webui-$(date +%F).tgz -C /data .
docker start open-webui
If you have many users, a PostgreSQL database (DATABASE_URL) is easier to back up and run highly available. Check the docs for your version before you migrate.
Troubleshooting quick reference
| Symptom | Likely cause | Check |
|---|---|---|
| Model list is empty | Wrong base URL, missing /v1, bad API key | docker logs open-webui, then the curl calls from step 1 from inside the container |
| Answers appear all at once | Proxy buffering | proxy_buffering off, and check any gateway in between |
| "Connection lost" or the UI keeps reconnecting | WebSocket upgrade blocked | Upgrade/Connection headers on the proxy |
| Everyone logged out after a redeploy | WEBUI_SECRET_KEY changed | Set a fixed key from your secret store |
| Env change ignored | Persistent config | Section 3 |
Prevent it next time
- Pin an image tag instead of
:main, and test upgrades on a copy of the data volume first. - Keep the container port bound to localhost or a private network. Only the proxy should be exposed.
- Keep
WEBUI_SECRET_KEY, the backend API key and the OIDC secret in a secret store, and include them in your restore test. - Review pending users and admin role holders monthly.
- Alert separately on
/healthand on the model backend, so you can tell whether the UI or the model is down.