Self-Hosting a Chat UI for Your LLMs with OpenWebUI
A self-hosted chat interface that talks to any OpenAI-compatible endpoint. With OIDC SSO, document search, and web browsing. Here's how it fits into a homelab LLM stack.
For a while I was using LLMs through a terminal. curl commands, API keys in environment variables, JSON in and JSON out. It worked, but it felt like reading a book through a keyhole. No conversation history worth looking at, no way to attach a file, no way to share a chat with someone who doesn't live in a terminal.
I tried a few chat UIs. Most of them fell into two camps: too simple to be useful, or too opinionated about which provider you should use. Then I found OpenWebUI, and it's been my daily driver ever since.
This is how I set it up, what makes it different from other options, and the few things I had to figure out the hard way.
What OpenWebUI actually is
Most people compare it to ChatGPT. That's roughly right but undersells it. OpenWebUI is a self-hosted web interface for LLMs that doesn't care where your models live. You point it at an OpenAI-compatible endpoint, and it gives you a polished chat experience with conversation history, file uploads, document search, web browsing, and a model picker.
The "OpenAI-compatible endpoint" part is doing a lot of work there. It means you can point it at OpenAI directly, or at a local Ollama instance, or at an LLM gateway like the LiteLLM setup I wrote about earlier. OpenWebUI doesn't need to know the difference.
In my case, I point it at my gateway. Every model I've configured there, local and cloud, shows up in the dropdown. One UI, all my models, no provider lock-in.
The stack
Three containers in the compose:
| Container | What it does |
|---|---|
| OpenWebUI | The web app itself |
| Playwright sidecar | Headless browser for the "browse the web" feature |
| Apache Tika sidecar | Document parsing for PDF, Office files, and other formats |
The sidecars are optional but worth it. Without Tika, file uploads are limited to plain text and images. Without Playwright, the web search feature can't render JavaScript-heavy pages.
Deployment
services:
openwebui:
image: ghcr.io/open-webui/open-webui:latest
container_name: openwebui
restart: always
security_opt:
- no-new-privileges:true
depends_on:
- openwebui-playwright
ports:
- "3000:8080"
environment:
ENABLE_SIGNUP: "False"
ENABLE_LOGIN_FORM: "False"
ENABLE_OAUTH_SIGNUP: "True"
# OIDC config (see authentication section below)
OPENID_PROVIDER_URL: https://your-idp/.well-known/openid-configuration
OAUTH_CLIENT_ID: your-client-id
OAUTH_CLIENT_SECRET: ${OAUTH_CLIENT_SECRET}
OAUTH_PROVIDER_NAME: SSO
WEBUI_URL: https://openwebui.example.com
STORAGE_PROVIDER: local
WEB_LOADER_ENGINE: playwright
PLAYWRIGHT_WS_URL: ws://openwebui-playwright:3000
volumes:
- openwebui-data:/app/backend/data
openwebui-playwright:
image: mcr.microsoft.com/playwright:latest
container_name: openwebui-playwright
restart: always
command: npx -y playwright run-server --port 3000 --host 0.0.0.0
tika:
image: apache/tika:latest
container_name: openwebui-tika
restart: always
volumes:
openwebui-data:
Same rules as my other posts: pin by SHA digest in production. :latest here is for readability.
ENABLE_SIGNUP: "False" and ENABLE_LOGIN_FORM: "False" are important. You don't want random people creating accounts on your LLM UI. All authentication goes through OIDC.
STORAGE_PROVIDER: local means uploaded files and chat history live on the filesystem inside the container. If you want S3-compatible storage, OpenWebUI supports that too. Local is simpler and works fine for a personal or small-team setup.
Connecting to a gateway
If you read my LiteLLM article, this is where it pays off. OpenWebUI doesn't need direct access to any provider. It just talks to the gateway.
In the OpenWebUI admin settings, under Connections, add your gateway as an OpenAI API connection:
URL: http://your-gateway:4000/v1
API Key: sk-your-gateway-key
That's it. Every model you configured in the gateway appears in the model dropdown. Users can pick between a cheap local model for quick questions and a cloud model for complex tasks, all from the same chat interface. They have no idea what's behind it, and they shouldn't need to.
One thing to watch for: OpenWebUI caches the model list. If you add a new model to your gateway, you may need to refresh the connection in the admin panel before it shows up.
Authentication with OIDC
This is where things get interesting if you're running more than one self-hosted service. I don't want separate logins for every tool. OpenWebUI supports OAuth/OIDC natively, which means you can wire it to a unified identity provider and get SSO across your entire homelab.
I wrote about this approach in my Pocket ID article. The short version: run an OIDC provider, create a client for OpenWebUI, and set these environment variables:
ENABLE_OAUTH_SIGNUP: "True"
OAUTH_MERGE_ACCOUNTS_BY_EMAIL: "True"
OPENID_PROVIDER_URL: https://your-idp/.well-known/openid-configuration
OAUTH_CLIENT_ID: your-client-id
OAUTH_CLIENT_SECRET: ${OAUTH_CLIENT_SECRET}
OAUTH_PROVIDER_NAME: Your IdP Name
WEBUI_URL: https://openwebui.example.com
OAUTH_MERGE_ACCOUNTS_BY_EMAIL: "True" is useful if you ever migrate from password auth to OIDC. Existing accounts get linked to their OIDC identity by email, so nobody loses their chat history.
OAUTH_UPDATE_PICTURE_ON_LOGIN: "True" is a nice touch. It pulls the avatar from your identity provider on each login, so profile pictures stay in sync across all your services.
The result: visiting OpenWebUI redirects to your identity provider. Authenticate once, and you're in. If you're already logged in from another service using the same provider, it's a single click.
Document search (RAG)
This is the feature I didn't know I needed. OpenWebUI has built-in retrieval-augmented generation. You upload documents (PDFs, text files, code), and when you ask a question, the model can search through them to find relevant context before answering.
The Tika sidecar handles document parsing. It extracts text from PDFs, Word documents, spreadsheets, and a long list of other formats. Without it, you're limited to plain text and images.
I use this for technical documentation. Instead of pasting chunks of a spec into a chat window, I upload the whole document, tag it in a collection, and ask questions. The model pulls the relevant sections automatically. It's not perfect, and you still need to verify answers against the source, but it's dramatically faster than ctrl+F through a 200-page PDF.
Web search
The Playwright sidecar gives OpenWebUI the ability to browse the web during a conversation. When the model needs current information, it can search, read pages, and cite what it found.
This is why the Playwright container exists. Headless Chrome renders the page properly, including JavaScript-heavy sites that a simple HTTP fetch would miss. It's slower than a plain text search but more reliable.
You can also configure OpenWebUI to use a self-hosted search engine like SearXNG as its search backend. That keeps your searches private and avoids rate limits on public APIs.
Multi-user setup
OpenWebUI was designed for multiple users from the start. Each person gets their own workspace, chat history, and document collections. Admins can control which models are available to which users.
If you're running this for a team, the model access controls matter. You might want everyone to have access to the cheap local model but restrict the expensive cloud models to specific people. OpenWebUI's admin panel handles this through role-based access.
With OIDC authentication, user management is automatic. When someone logs in through your identity provider, an account is created. When they're removed from the provider, they lose access. No manual provisioning.
Things I learned
The model list cache got me once. I added a new model to the gateway and kept refreshing the OpenWebUI dropdown. Nothing showed up. You need to go into admin settings, edit the connection, and let it re-fetch. Once you know this, it's a five-second fix. Until you know it, it's confusing.
Tika is memory-hungry. The container will happily eat 2GB+ when processing large documents. Give it room or it'll OOM on you. I started with 512MB and it crashed on a 50-page PDF.
Local storage is fine until it isn't. If you're running this for more than a handful of people, watch the disk usage. Chat history and uploaded files add up. S3-compatible storage is the better long-term play if you have the infrastructure for it.
The web browser feature is slow by design. It's spinning up a real browser, rendering a page, and extracting content. Don't expect sub-second responses when browsing is involved. The quality makes up for it.
Wrapping up
OpenWebUI is the front door to my LLM setup. Behind it sits a gateway that handles routing and caching. In front of it sits SSO so I don't manage passwords. Each piece does one job well, and they connect through standard interfaces: OpenAI-compatible API for models, OIDC for auth.
That's the thing about this stack. Nothing is custom. Everything speaks a standard protocol. If I wanted to swap OpenWebUI for a different UI tomorrow, the gateway wouldn't care. If I wanted to swap the identity provider, OpenWebUI wouldn't care. The modularity is the value.
If you're running LLMs locally, you need a good interface. OpenWebUI is the best one I've found, and the gap between it and the alternatives is significant.