Motivation

DeepSeek R1 has been my favorite LLM since its release. However, due to the explosive growth in its user base and usage, its official chat service has become quite unstable, frequently displaying messages like “The server is busy. Please try again later.” Upon a friend’s recommendation, I decided to explore using third-party DeepSeek APIs—preferably with the flexibility to switch between different LLMs or even use locally deployed ones. Open WebUI has perfectly met these requirements while also supporting plugins to extend its functionality.

Furthermore, since I frequently switch between multiple computers, running and maintaining individual Open WebUI instances on each device is impractical—not to mention the lack of chat history synchronization. Given my existing Home Lab setup, I decided to deploy Open WebUI on my Kubernetes cluster and securely access it via Cloudflare Tunnels.

Installation

Create a separate namespace for Open WebUI

1
2
3
4
apiVersion: v1
kind: Namespace
metadata:
  name: open-webui

Leverage Metallb to create a load balancer for it. Within the home network, I can access it at http://<LB-IP>:8080 or I can assign a local domain name for it like ai.home with Pi-hole

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: v1
kind: Service
metadata:
  name: open-webui-tcp
  namespace: open-webui
spec:
  type: LoadBalancer
  loadBalancerIP: <LB-IP>
  ports:
  - name: web-ui
    protocol: TCP
    port: 8080
    targetPort: 8080
  selector:
    app: open-webui

Create a PersistentVolume on the NFS server for Open WebUI DB, cache, uploads etc.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv-open-webui
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /nfs/export/open-webui
    server: <NFS-server-IP>

Create a PersistentVolumeClaim for the pod

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: open-webui-pvc
  namespace: open-webui
spec:
  accessModes:
    - ReadWriteMany
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi
  storageClassName: slow

Finally, create the Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
apiVersion: apps/v1
kind: Deployment
metadata:
  name: open-webui
  namespace: open-webui
spec:
  selector:
    matchLabels:
      app: open-webui
  replicas: 1
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: open-webui
    spec:
      containers:
      - name: open-webui
        image: ghcr.io/open-webui/open-webui:v0.5.16
        imagePullPolicy: IfNotPresent
        env:
        - name: WEBUI_AUTH
          value: "False" # no need to login to Open WebUI
        - name: ENABLE_RAG_WEB_SEARCH
          value: "True"
        ports:
        - containerPort: 8080
          name: web-ui
        resources:
          limits:
            cpu: 2
            memory: 3Gi
          requests:
            cpu: 1
            memory: 1Gi
        livenessProbe:
          httpGet:
            scheme: HTTP
            path: /health
            port: web-ui
          initialDelaySeconds: 180
          periodSeconds: 10
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
        readinessProbe:
          httpGet:
            scheme: HTTP
            path: /
            port: web-ui
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
        volumeMounts:
        - name: data
          mountPath: /app/backend/data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: open-webui-pvc

That’s it.

Configure Cloudflare Tunnels

NetworkChuck provided a pretty concise video guidance on how to setup Cloudflare Tunnels so you could securely access services in your home network. I already have cloudflared installed, thus just need to add one more public hostname: Cloudflare Tunnels

And now I can access my “stable” DeepSeek (or other LLM) from anywhere! Open WebUI