[GH-ISSUE #20444] Docker hot-reload development setup #34717

Closed
opened 2026-04-25 08:49:15 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @vlthr on GitHub (Jan 7, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/20444

Summary

Create a Docker-based development workflow with hot-reload for both frontend (Vite/Svelte) and backend (uvicorn/Python), eliminating the need to rebuild the image on every code change.

Goals

  • Hot-reload for frontend changes (Vite HMR)
  • Hot-reload for backend changes (uvicorn --reload)
  • Share data volume with production compose
  • Support OrbStack HTTPS access

Files Created

  • Dockerfile.dev - Development image with Node.js 22 + Python 3.11
  • docker-compose.dev.yaml - Dev compose with volume mounts
  • scripts/dev-entrypoint.sh - Entrypoint running both servers

Usage

docker compose -f docker-compose.dev.yaml up --build  # First time
docker compose -f docker-compose.dev.yaml up          # Subsequent runs

Access via https://open-webui.open-webui.orb.local or http://localhost:5173

Originally created by @vlthr on GitHub (Jan 7, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/20444 ## Summary Create a Docker-based development workflow with hot-reload for both frontend (Vite/Svelte) and backend (uvicorn/Python), eliminating the need to rebuild the image on every code change. ## Goals - Hot-reload for frontend changes (Vite HMR) - Hot-reload for backend changes (uvicorn --reload) - Share data volume with production compose - Support OrbStack HTTPS access ## Files Created - `Dockerfile.dev` - Development image with Node.js 22 + Python 3.11 - `docker-compose.dev.yaml` - Dev compose with volume mounts - `scripts/dev-entrypoint.sh` - Entrypoint running both servers ## Usage ```bash docker compose -f docker-compose.dev.yaml up --build # First time docker compose -f docker-compose.dev.yaml up # Subsequent runs ``` Access via `https://open-webui.open-webui.orb.local` or `http://localhost:5173`
Author
Owner

@vlthr commented on GitHub (Jan 7, 2026):

Progress Notes

Research Phase

  • Explored native dev setup (npm run dev + backend/dev.sh)
  • Found src/lib/constants.ts auto-detects dev mode and points to port 8080
  • No Vite proxy needed by default - frontend knows to talk to backend directly

Process Management

  • Initial approach using sh -c "cmd1 & cmd2" has problems (no signal propagation, no crash detection)
  • Solution: Use --init flag (adds tini as PID 1) + bash wrapper with wait -n

File Watching on macOS Docker

  • OrbStack: Native inotify support works - polling NOT needed
  • Docker Desktop: Unreliable inotify - requires WATCHFILES_FORCE_POLLING=true and CHOKIDAR_USEPOLLING=true

Volume Strategy

  • Mount entire project directory .:/app:cached for simplicity
  • Anonymous volume - /app/node_modules preserves container's Linux binaries
  • Named volume open-webui:/app/backend/data shares data with production compose
<!-- gh-comment-id:3718549872 --> @vlthr commented on GitHub (Jan 7, 2026): ## Progress Notes ### Research Phase - Explored native dev setup (`npm run dev` + `backend/dev.sh`) - Found `src/lib/constants.ts` auto-detects dev mode and points to port 8080 - No Vite proxy needed by default - frontend knows to talk to backend directly ### Process Management - Initial approach using `sh -c "cmd1 & cmd2"` has problems (no signal propagation, no crash detection) - Solution: Use `--init` flag (adds tini as PID 1) + bash wrapper with `wait -n` ### File Watching on macOS Docker - **OrbStack**: Native inotify support works - polling NOT needed - **Docker Desktop**: Unreliable inotify - requires `WATCHFILES_FORCE_POLLING=true` and `CHOKIDAR_USEPOLLING=true` ### Volume Strategy - Mount entire project directory `.:/app:cached` for simplicity - Anonymous volume `- /app/node_modules` preserves container's Linux binaries - Named volume `open-webui:/app/backend/data` shares data with production compose
Author
Owner

@vlthr commented on GitHub (Jan 7, 2026):

HTTPS Access via OrbStack

Problem

Accessing via OrbStack's HTTPS URL (https://open-webui.open-webui.orb.local) causes mixed content errors - the frontend tries to make HTTP requests to port 8080.

Options Considered

  1. CORS only - Works for http://localhost:5173 but not HTTPS (mixed content)
  2. Vite proxy - Routes API through frontend, same-origin requests, no mixed content
  3. OrbStack multi-port HTTPS - Investigated but not supported (see below)

OrbStack Limitation

Researched whether OrbStack container labels could terminate HTTPS on multiple ports. Not supported.

  • dev.orbstack.http-port only allows ONE port for HTTPS termination
  • Feature request: orbstack/orbstack#1165 (29+ upvotes, still open)
  • No Traefik-style domain@port syntax available

Solution: Vite Proxy (env var controlled)

Added optional proxy support to vite.config.ts via VITE_API_PROXY_TARGET env var:

  • When set, Vite proxies /api, /ollama, /openai, /socket.io to backend
  • PUBLIC_API_BASE_URL= (empty) makes frontend use same-origin requests
  • No changes to default behavior - proxy only activates when env var is set

Vite Bug

__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS env var doesn't work unless allowedHosts array already exists in config. Had to hardcode .open-webui.orb.local in vite.config.ts for now.

<!-- gh-comment-id:3718550693 --> @vlthr commented on GitHub (Jan 7, 2026): ## HTTPS Access via OrbStack ### Problem Accessing via OrbStack's HTTPS URL (`https://open-webui.open-webui.orb.local`) causes mixed content errors - the frontend tries to make HTTP requests to port 8080. ### Options Considered 1. **CORS only** - Works for `http://localhost:5173` but not HTTPS (mixed content) 2. **Vite proxy** - Routes API through frontend, same-origin requests, no mixed content 3. **OrbStack multi-port HTTPS** - Investigated but not supported (see below) ### OrbStack Limitation Researched whether OrbStack container labels could terminate HTTPS on multiple ports. **Not supported.** - `dev.orbstack.http-port` only allows ONE port for HTTPS termination - Feature request: [orbstack/orbstack#1165](https://github.com/orbstack/orbstack/issues/1165) (29+ upvotes, still open) - No Traefik-style `domain@port` syntax available ### Solution: Vite Proxy (env var controlled) Added optional proxy support to `vite.config.ts` via `VITE_API_PROXY_TARGET` env var: - When set, Vite proxies `/api`, `/ollama`, `/openai`, `/socket.io` to backend - `PUBLIC_API_BASE_URL=` (empty) makes frontend use same-origin requests - No changes to default behavior - proxy only activates when env var is set ### Vite Bug `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var doesn't work unless `allowedHosts` array already exists in config. Had to hardcode `.open-webui.orb.local` in vite.config.ts for now.
Author
Owner

@vlthr commented on GitHub (Jan 7, 2026):

Changes to Upstream Files

vite.config.ts

  • Added optional VITE_API_PROXY_TARGET env var support for API proxying
  • Added allowedHosts: ['.open-webui.orb.local'] for OrbStack access

src/lib/constants.ts

  • Added PUBLIC_API_BASE_URL env var override for API base URL
  • Default behavior unchanged - only activates when env var is set

These changes add flexibility without affecting default behavior and could be upstreamed.

Local-only Files

  • Dockerfile.dev
  • docker-compose.dev.yaml
  • scripts/dev-entrypoint.sh
<!-- gh-comment-id:3718551186 --> @vlthr commented on GitHub (Jan 7, 2026): ## Changes to Upstream Files ### `vite.config.ts` - Added optional `VITE_API_PROXY_TARGET` env var support for API proxying - Added `allowedHosts: ['.open-webui.orb.local']` for OrbStack access ### `src/lib/constants.ts` - Added `PUBLIC_API_BASE_URL` env var override for API base URL - Default behavior unchanged - only activates when env var is set These changes add flexibility without affecting default behavior and could be upstreamed. ## Local-only Files - `Dockerfile.dev` - `docker-compose.dev.yaml` - `scripts/dev-entrypoint.sh`
Author
Owner

@vlthr commented on GitHub (Jan 7, 2026):

Development setup complete and working. Access via https://open-webui.open-webui.orb.local or http://localhost:5173.

<!-- gh-comment-id:3718551456 --> @vlthr commented on GitHub (Jan 7, 2026): Development setup complete and working. Access via `https://open-webui.open-webui.orb.local` or `http://localhost:5173`.
Author
Owner

@vlthr commented on GitHub (Jan 7, 2026):

Final Summary - Decisions & Fixes

Files Created

  • Dockerfile.dev - Node 22 + Python 3.11 dev image
  • docker-compose.dev.yaml - Dev compose with volume mounts
  • scripts/dev-entrypoint.sh - Runs both servers with proper signal handling

Key Fixes

Process Management

  • Use --init flag (tini) for signal propagation
  • wait -n in bash for crash detection
  • Backend starts first, wait for port 8080, then frontend

Static Files

  • config.py deletes STATIC_DIR contents at import then copies from FRONTEND_BUILD_DIR/static
  • Fix: Set FRONTEND_BUILD_DIR=/app so it copies from /app/static

HTTPS via OrbStack

  • Vite proxy routes /api, /ollama, /openai, /ws/socket.io to backend
  • PUBLIC_API_BASE_URL= (empty) makes frontend use same-origin
  • allowedHosts: ['.open-webui.orb.local'] in vite.config.ts
  • __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS env var is buggy - doesn't work without existing array

WebSocket

  • Backend uses /ws/socket.io path (not /socket.io)
  • Vite proxy must match /ws/socket.io

LiveKit

  • LIVEKIT_URL=ws://livekit:7880 - internal Docker URL for backend
  • LIVEKIT_PUBLIC_URL=wss://livekit.open-webui.orb.local - browser uses OrbStack HTTPS

Cold Start Optimization

  • BYPASS_EMBEDDING_AND_RETRIEVAL=true - skip SentenceTransformer loading
  • ENABLE_BASE_MODELS_CACHE=false - skip model pre-caching

OrbStack vs Docker Desktop

  • OrbStack: Native inotify works, no polling needed
  • Docker Desktop: May need WATCHFILES_FORCE_POLLING=true + CHOKIDAR_USEPOLLING=true

Volume Strategy

  • Mount .:/app:cached for hot reload
  • Anonymous volume /app/node_modules preserves Linux binaries
  • Named volume open-webui:/app/backend/data shares with production
<!-- gh-comment-id:3718712110 --> @vlthr commented on GitHub (Jan 7, 2026): ## Final Summary - Decisions & Fixes ### Files Created - `Dockerfile.dev` - Node 22 + Python 3.11 dev image - `docker-compose.dev.yaml` - Dev compose with volume mounts - `scripts/dev-entrypoint.sh` - Runs both servers with proper signal handling ### Key Fixes **Process Management** - Use `--init` flag (tini) for signal propagation - `wait -n` in bash for crash detection - Backend starts first, wait for port 8080, then frontend **Static Files** - `config.py` deletes STATIC_DIR contents at import then copies from `FRONTEND_BUILD_DIR/static` - Fix: Set `FRONTEND_BUILD_DIR=/app` so it copies from `/app/static` **HTTPS via OrbStack** - Vite proxy routes `/api`, `/ollama`, `/openai`, `/ws/socket.io` to backend - `PUBLIC_API_BASE_URL=` (empty) makes frontend use same-origin - `allowedHosts: ['.open-webui.orb.local']` in vite.config.ts - `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var is buggy - doesn't work without existing array **WebSocket** - Backend uses `/ws/socket.io` path (not `/socket.io`) - Vite proxy must match `/ws/socket.io` **LiveKit** - `LIVEKIT_URL=ws://livekit:7880` - internal Docker URL for backend - `LIVEKIT_PUBLIC_URL=wss://livekit.open-webui.orb.local` - browser uses OrbStack HTTPS **Cold Start Optimization** - `BYPASS_EMBEDDING_AND_RETRIEVAL=true` - skip SentenceTransformer loading - `ENABLE_BASE_MODELS_CACHE=false` - skip model pre-caching **OrbStack vs Docker Desktop** - OrbStack: Native inotify works, no polling needed - Docker Desktop: May need `WATCHFILES_FORCE_POLLING=true` + `CHOKIDAR_USEPOLLING=true` **Volume Strategy** - Mount `.:/app:cached` for hot reload - Anonymous volume `/app/node_modules` preserves Linux binaries - Named volume `open-webui:/app/backend/data` shares with production
Author
Owner

@vlthr commented on GitHub (Jan 7, 2026):

Updated with final summary of all decisions and fixes.

<!-- gh-comment-id:3718712474 --> @vlthr commented on GitHub (Jan 7, 2026): Updated with final summary of all decisions and fixes.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#34717