Post

Open WebUI

Open WebUI

Open WebUI

Self-hosted chat interface for a remote Ollama instance, with retrieval-augmented web search. Runs in the default namespace of the k3s cluster, fronted by Traefik.

Deployment

ItemValue
Imageghcr.io/open-webui/open-webui:latest
Namespacedefault
Replicas1 (Recreate strategy)
Node placementPinned to cm4-left via nodeSelector
ServiceClusterIP, port 8080
IngressTraefik IngressRoutechat.prod-k3s.iot-proxmox.net / www.chat.prod-k3s.iot-proxmox.net, TLS via the shared local-example-com-tls wildcard cert
Storageopen-webui-data PVC, 5Gi, longhorn storage class, mounted at /app/backend/data
Auto-reloadreloader.stakater.com/auto: "true"

Configuration

Open WebUI points at an external Ollama server rather than running its own model runtime:

Env varValue
OLLAMA_BASE_URLhttps://ollama.intel.iot-proxmox.net
ENABLE_RAG_WEB_SEARCHTrue
RAG_WEB_SEARCH_ENGINEsearxng
RAG_WEB_SEARCH_RESULT_COUNT3
RAG_WEB_SEARCH_CONCURRENT_REQUESTS10
WEBUI_SECRET_KEYSourced from the open-webui-secrets Secret
TZAmerica/New_York

Why longhorn and not local-path

The PVC uses the longhorn storage class rather than local-path. The volume holds real, growing user data (chat history, accounts, uploaded documents, webui.db) and the pod isn’t pinned by node affinity for scheduling purposes in a way that guarantees it always lands on the same node as a local-path volume — longhorn replicates across nodes and binds Immediate, avoiding the failure mode where a local-path PV is stranded on a downed node.

WEBUI_SECRET_KEY gotcha

If WEBUI_SECRET_KEY isn’t set explicitly, Open WebUI writes an auto-generated key to /app/backend/.webui_secret_key — outside the PVC-backed DATA_DIR. Every pod restart would then regenerate the key and invalidate all logged-in sessions. This is worked around by setting WEBUI_SECRET_KEY explicitly via the open-webui-secrets Secret; rotating that secret logs every user out.

Readiness/liveness probes

Cold start downloads the RAG embedding model (sentence-transformers/all-MiniLM-L6-v2) from Hugging Face before the server starts listening — this took roughly 10 minutes on first boot, hence generous probe settings (initialDelaySeconds: 30, failureThreshold: 60 on both probes against GET /health). Without this, Traefik routes to the pod before it’s ready and every request 502s until the download finishes.

Migrated from Docker Compose

Originally ran as a Docker container on a separate Intel NUC host (chat.intel.iot-proxmox.net). Converted to this cluster’s Kubernetes pattern (Deployment + ClusterIP Service + PVC + Traefik IngressRoute) with the hostname updated to chat.prod-k3s.iot-proxmox.net to match every other app already migrated. PUID/PGID and host-path /etc/localtime mounts were dropped (not applicable to this image or a multi-node cluster) in favor of a TZ env var.

This post is licensed under CC BY 4.0 by the author.