infra: deploy Open WebUI on hf-pdocker-01 with Ollama + Claude backends
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
# Open WebUI Deployment Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Deploy Open WebUI on hf-pdocker-01 with Ollama + Anthropic Claude backends, publicly accessible at `open.hobbs.farm`.
|
||||
|
||||
**Architecture:** Single-container Compose service on hf-pdocker-01. Traefik file-based dynamic config for HTTPS routing. NAS-backed persistent storage. Prometheus blackbox probes for monitoring.
|
||||
|
||||
**Tech Stack:** Open WebUI (ghcr.io/open-webui/open-webui:main), Traefik v3.6 (file provider), Prometheus blackbox exporter.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-03-11-openwebui-design.md`
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1: Create Files and Deploy
|
||||
|
||||
### Task 1: Create Compose File
|
||||
|
||||
**Files:**
|
||||
- Create: `compose/hf-pdocker-01/openwebui/docker-compose.yml`
|
||||
|
||||
- [ ] **Step 1: Create the compose file**
|
||||
|
||||
```yaml
|
||||
services:
|
||||
open-webui:
|
||||
image: ghcr.io/open-webui/open-webui:main
|
||||
container_name: open-webui
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3010:8080"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- TZ=America/Chicago
|
||||
- OLLAMA_BASE_URL=http://192.168.86.196:11434
|
||||
volumes:
|
||||
- /mnt/docker-data/configs/openwebui:/app/backend/data
|
||||
mem_limit: 1G
|
||||
memswap_limit: 1G
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=false"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -sf http://localhost:8080/health || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "3"
|
||||
```
|
||||
|
||||
### Task 2: Create Traefik Dynamic Config
|
||||
|
||||
**Files:**
|
||||
- Create: `stacks/traefik/dynamic/openwebui.yml`
|
||||
|
||||
- [ ] **Step 1: Create the Traefik dynamic route file**
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
openwebui:
|
||||
rule: "Host(`open.hobbs.farm`)"
|
||||
entryPoints:
|
||||
- websecure
|
||||
service: openwebui
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
openwebui-http:
|
||||
rule: "Host(`open.hobbs.farm`)"
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- openwebui-https-redirect
|
||||
service: openwebui
|
||||
middlewares:
|
||||
openwebui-https-redirect:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
permanent: true
|
||||
services:
|
||||
openwebui:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://192.168.86.192:3010"
|
||||
```
|
||||
|
||||
### Task 3: Deploy to hf-pdocker-01
|
||||
|
||||
Uses `docker-swarm-ops` agent. All steps run on hf-pdocker-01 via SSH.
|
||||
|
||||
- [ ] **Step 1: Create data directory on NAS**
|
||||
|
||||
```bash
|
||||
ssh glyptic@192.168.86.192 "sudo mkdir -p /mnt/docker-data/configs/openwebui"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Create compose directory on NAS**
|
||||
|
||||
```bash
|
||||
ssh glyptic@192.168.86.192 "sudo mkdir -p /mnt/docker-data/compose/hf-pdocker-01/openwebui"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Generate WEBUI_SECRET_KEY and create .env file**
|
||||
|
||||
```bash
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
# Get ANTHROPIC_API_KEY from existing ai-agents .env on NAS
|
||||
API_KEY=$(ssh glyptic@192.168.86.192 "grep ANTHROPIC_API_KEY /mnt/docker-data/compose/hf-pdocker-01/ai-agents/.env | cut -d= -f2")
|
||||
ssh glyptic@192.168.86.192 "cat > /mnt/docker-data/compose/hf-pdocker-01/openwebui/.env << EOF
|
||||
ANTHROPIC_API_KEY=$API_KEY
|
||||
WEBUI_SECRET_KEY=$SECRET
|
||||
EOF"
|
||||
```
|
||||
|
||||
- [ ] **Step 4: SCP compose file to NAS**
|
||||
|
||||
```bash
|
||||
scp compose/hf-pdocker-01/openwebui/docker-compose.yml glyptic@192.168.86.192:/mnt/docker-data/compose/hf-pdocker-01/openwebui/docker-compose.yml
|
||||
```
|
||||
|
||||
- [ ] **Step 5: SCP Traefik dynamic config to NAS**
|
||||
|
||||
```bash
|
||||
scp stacks/traefik/dynamic/openwebui.yml glyptic@192.168.86.192:/mnt/docker-data/traefik/dynamic/openwebui.yml
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Pull image and start container**
|
||||
|
||||
```bash
|
||||
ssh glyptic@192.168.86.192 "cd /mnt/docker-data/compose/hf-pdocker-01/openwebui && docker compose pull && docker compose up -d"
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Verify container health**
|
||||
|
||||
```bash
|
||||
ssh glyptic@192.168.86.192 "docker ps --filter name=open-webui --format 'table {{.Names}}\t{{.Status}}'"
|
||||
```
|
||||
|
||||
Expected: `open-webui Up ... (healthy)` (may take up to 60s for healthcheck)
|
||||
|
||||
- [ ] **Step 8: Verify internal access**
|
||||
|
||||
```bash
|
||||
ssh glyptic@192.168.86.192 "curl -sf -o /dev/null -w '%{http_code}' http://localhost:3010"
|
||||
```
|
||||
|
||||
Expected: `200`
|
||||
|
||||
- [ ] **Step 9: Verify Traefik picked up the route**
|
||||
|
||||
```bash
|
||||
curl -sf -o /dev/null -w '%{http_code}' https://open.hobbs.farm
|
||||
```
|
||||
|
||||
Expected: `200` (Traefik auto-reloads file-based config)
|
||||
|
||||
- [ ] **Step 10: Verify Ollama connectivity from container**
|
||||
|
||||
```bash
|
||||
ssh glyptic@192.168.86.192 "docker exec open-webui curl -sf http://192.168.86.196:11434/api/tags | head -c 200"
|
||||
```
|
||||
|
||||
Expected: JSON response listing available Ollama models (llava:13b)
|
||||
|
||||
## Chunk 2: Monitoring and Documentation
|
||||
|
||||
### Task 4: Add Prometheus Blackbox Probes
|
||||
|
||||
**Files:**
|
||||
- Modify: `stacks/monitoring/prometheus.yml`
|
||||
|
||||
- [ ] **Step 1: Add internal probe to http-pdocker01 job**
|
||||
|
||||
Add `- http://192.168.86.192:3010 # Open WebUI` to the `http-pdocker01` targets list (after the Alertmanager line, line 70).
|
||||
|
||||
- [ ] **Step 2: Add external probe to http-external job**
|
||||
|
||||
Add `- https://open.hobbs.farm # Open WebUI` to the `http-external` targets list (after the Gitea line, line 158).
|
||||
|
||||
- [ ] **Step 3: SCP updated prometheus.yml to NAS**
|
||||
|
||||
```bash
|
||||
scp stacks/monitoring/prometheus.yml glyptic@192.168.86.192:/mnt/docker-data/configs/prometheus/prometheus.yml
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Hot-reload Prometheus**
|
||||
|
||||
```bash
|
||||
ssh glyptic@192.168.86.192 "curl -X POST http://localhost:9095/-/reload"
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Verify probes are up**
|
||||
|
||||
```bash
|
||||
ssh glyptic@192.168.86.192 "curl -s 'http://localhost:9095/api/v1/targets?state=active' | python3 -c \"import sys,json; targets=json.load(sys.stdin)['data']['activeTargets']; [print(t['labels'].get('instance','')) for t in targets if 'open' in t['labels'].get('instance','').lower()]\""
|
||||
```
|
||||
|
||||
Expected: Two lines showing `http://192.168.86.192:3010` and `https://open.hobbs.farm`
|
||||
|
||||
### Task 5: Update CLAUDE.md
|
||||
|
||||
**Files:**
|
||||
- Modify: `CLAUDE.md`
|
||||
|
||||
- [ ] **Step 1: Add Open WebUI to hf-pdocker-01 service list**
|
||||
|
||||
Add `├── [COMPOSE] Open WebUI (:3010) -- LLM chat UI (Ollama + Claude)` to the hf-pdocker-01 section.
|
||||
|
||||
- [ ] **Step 2: Add to internal services list**
|
||||
|
||||
Add `- Open WebUI: :3010` under hf-pdocker-01 internal services.
|
||||
|
||||
- [ ] **Step 3: Add to public services list**
|
||||
|
||||
Add `- Open WebUI: \`open.hobbs.farm\`` under Public Services.
|
||||
|
||||
### Task 6: Update MEMORY.md
|
||||
|
||||
**Files:**
|
||||
- Modify: `/home/glyptic/.claude/projects/-mnt-c-Users-Jeremy-projects-docker/memory/MEMORY.md`
|
||||
|
||||
- [ ] **Step 1: Add Open WebUI memory entry**
|
||||
|
||||
Add section with deployment details: node, port, compose path, LLM backends, Traefik route, data path.
|
||||
|
||||
### Task 7: Update Wiki
|
||||
|
||||
- [ ] **Step 1: Launch documentation-keeper agent (background)**
|
||||
|
||||
Update wiki with new Open WebUI service documentation.
|
||||
|
||||
### Task 8: Commit
|
||||
|
||||
- [ ] **Step 1: Commit all new/modified files**
|
||||
|
||||
```bash
|
||||
git add compose/hf-pdocker-01/openwebui/docker-compose.yml stacks/traefik/dynamic/openwebui.yml stacks/monitoring/prometheus.yml CLAUDE.md
|
||||
git commit -m "infra: deploy Open WebUI on hf-pdocker-01 with Ollama + Claude backends"
|
||||
```
|
||||
@@ -0,0 +1,148 @@
|
||||
# Open WebUI Deployment Design
|
||||
|
||||
**Date:** 2026-03-11
|
||||
**Node:** hf-pdocker-01 (192.168.86.192)
|
||||
**Public URL:** https://open.hobbs.farm
|
||||
**Internal URL:** http://192.168.86.192:3010
|
||||
|
||||
## Overview
|
||||
|
||||
Deploy Open WebUI as a standalone Compose service on hf-pdocker-01, connected to Ollama (local GPU machine) and Anthropic Claude API. Publicly accessible via Traefik at `open.hobbs.farm`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Internet -> Traefik (hf-pdocker-01:443) -> http://192.168.86.192:3010 -> open-webui:8080
|
||||
|
|
||||
+-> Ollama (192.168.86.196:11434) [llava:13b + future models]
|
||||
+-> Anthropic API (cloud) [Claude models]
|
||||
```
|
||||
|
||||
## Service Definition
|
||||
|
||||
- **Image:** `ghcr.io/open-webui/open-webui:main`
|
||||
- **Container name:** `open-webui`
|
||||
- **Internal port:** 8080 (container default)
|
||||
- **Host port:** 3010
|
||||
- **Memory:** `mem_limit: 1G`, `memswap_limit: 1G`
|
||||
- **Restart:** `unless-stopped`
|
||||
- **Watchtower:** `com.centurylinklabs.watchtower.enable=false` (manual upgrades)
|
||||
- **Timezone:** `TZ=America/Chicago`
|
||||
|
||||
### Compose Location
|
||||
|
||||
- Repo: `compose/hf-pdocker-01/openwebui/docker-compose.yml`
|
||||
- Deploy target: `/mnt/docker-data/compose/hf-pdocker-01/openwebui/`
|
||||
- `.env` file alongside compose for secrets (ANTHROPIC_API_KEY, WEBUI_SECRET_KEY)
|
||||
|
||||
### Persistent Storage
|
||||
|
||||
- Data volume: `/mnt/docker-data/configs/openwebui/` -> `/app/backend/data` in container
|
||||
- Stores: user accounts, chat history, settings, uploaded files
|
||||
- Open WebUI uses SQLite internally. Single-writer scenario on NFS is acceptable. If locking issues arise, fall back to local storage (like Guacamole's MySQL pattern at `/var/lib/`).
|
||||
- Container runs as root by default; NFS write permissions should work without `user: "1024:100"` but will be verified during deploy.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `OLLAMA_BASE_URL=http://192.168.86.196:11434` — native Ollama connection
|
||||
- `ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}` — from .env, enables native Anthropic provider (Open WebUI v0.5+ has built-in Anthropic support)
|
||||
- `WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY}` — from .env, generated via `openssl rand -hex 32`
|
||||
- `TZ=America/Chicago`
|
||||
|
||||
### Healthcheck
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -sf http://localhost:8080/health || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
```yaml
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "3"
|
||||
```
|
||||
|
||||
### Traefik Configuration
|
||||
|
||||
Traefik on hf-pdocker-01 uses **file-based dynamic config** (no Docker provider/labels). A new dynamic config file is needed:
|
||||
|
||||
- **Repo path:** `stacks/traefik/dynamic/openwebui.yml`
|
||||
- **Deploy target:** `/mnt/docker-data/traefik/dynamic/openwebui.yml` (auto-picked up by Traefik's file watcher)
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
openwebui:
|
||||
rule: "Host(`open.hobbs.farm`)"
|
||||
entryPoints:
|
||||
- websecure
|
||||
service: openwebui
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
openwebui-http:
|
||||
rule: "Host(`open.hobbs.farm`)"
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- openwebui-https-redirect
|
||||
service: openwebui
|
||||
middlewares:
|
||||
openwebui-https-redirect:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
permanent: true
|
||||
services:
|
||||
openwebui:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://192.168.86.192:3010"
|
||||
```
|
||||
|
||||
### Network
|
||||
|
||||
- Default bridge network (single-container service, no custom network needed)
|
||||
- Traefik routes to `http://192.168.86.192:3010` via file-based config (no overlay network required)
|
||||
|
||||
### Authentication
|
||||
|
||||
- Built-in Open WebUI auth (email/password)
|
||||
- First user to register becomes admin
|
||||
- Admin disables open registration after initial setup
|
||||
|
||||
### Monitoring
|
||||
|
||||
- **Internal probe:** `http://192.168.86.192:3010` added to Prometheus `http-pdocker01` blackbox job
|
||||
- **External probe:** `https://open.hobbs.farm` added to Prometheus `http-external` blackbox job
|
||||
|
||||
## LLM Backend Details
|
||||
|
||||
### Ollama (Native)
|
||||
|
||||
Open WebUI has first-class Ollama support. Setting `OLLAMA_BASE_URL` makes all Ollama models available in the model selector. Currently `llava:13b` is loaded; any future models pulled to Ollama will auto-appear.
|
||||
|
||||
### Anthropic Claude (Native)
|
||||
|
||||
Open WebUI v0.5+ has built-in Anthropic provider support. Setting the `ANTHROPIC_API_KEY` environment variable enables Claude models in the model selector. No proxy or OpenAI-compatibility layer needed. The existing API key (same one used by frigate-notify and AI agents) goes in the `.env` file.
|
||||
|
||||
## Post-Deploy Checklist
|
||||
|
||||
1. Verify container health and web UI loads at `http://192.168.86.192:3010`
|
||||
2. Verify Traefik routes `https://open.hobbs.farm` correctly
|
||||
3. Register first admin user
|
||||
4. Disable open registration in admin settings
|
||||
5. Verify Ollama models appear in model selector
|
||||
6. Verify Claude models appear in model selector
|
||||
7. Add Prometheus blackbox probes (internal + external)
|
||||
8. Hot-reload Prometheus: `curl -X POST http://192.168.86.192:9095/-/reload`
|
||||
9. Update CLAUDE.md service lists
|
||||
10. Update MEMORY.md
|
||||
11. Update wiki via documentation-keeper agent
|
||||
12. Store WEBUI_SECRET_KEY in Vaultwarden
|
||||
Reference in New Issue
Block a user