Skip to content

Comments

feat: add Docker + web deployment support#161

Open
ageofalgo wants to merge 1 commit intojamiepine:mainfrom
ageofalgo:feat/docker-web-deployment
Open

feat: add Docker + web deployment support#161
ageofalgo wants to merge 1 commit intojamiepine:mainfrom
ageofalgo:feat/docker-web-deployment

Conversation

@ageofalgo
Copy link

feat: Docker + Web Deployment Support

Summary

Adds production-ready Docker support for running Voicebox as a self-contained web service (API + UI) without the Tauri desktop app. This implements the Docker deployment planned in DOCKER_DEPLOYMENT.md.

Motivation

Currently, Voicebox can only be used via the Tauri desktop app or by running the backend manually. This PR enables a simple docker compose up workflow that serves both the API and the web frontend from a single container — useful for headless servers, integration with other local tools, and anyone who doesn't want to install Python/Bun/Rust locally.

Changes

New Files

File Description
Dockerfile 3-stage build: (1) Bun builds the web/ frontend, (2) Python deps compiled in builder, (3) slim runtime image with both frontend + backend
docker-compose.yml Localhost-only port binding (17493), bind-mount for audio output, named volumes for data persistence and HuggingFace model cache
.dockerignore Excludes desktop-only dirs (tauri/, landing/, docs/) and build artifacts for a lean image

Modified Files

File Change
backend/main.py Root GET / endpoint now serves index.html when the frontend build is present (Docker), falls back to API JSON otherwise. Added conditional static file serving + SPA catch-all route, only activated when /app/frontend/ exists — zero impact on existing non-Docker usage.

Usage

# Build image
docker compose build

# Start container
docker compose up -d

# Open in browser
open http://localhost:17493

# Check health
curl http://localhost:17493/health

# View logs
docker compose logs -f

# Stop
docker compose down

Generated audio files appear in the ./output/ directory (configurable via the bind-mount in docker-compose.yml).

Architecture

┌──────────────────────────────────────────┐
│            Docker Container              │
│                                          │
│  ┌──────────────────────────────────┐    │
│  │  FastAPI (uvicorn :17493)        │    │
│  │                                  │    │
│  │  GET /         → index.html      │    │
│  │  GET /assets/* → static files    │    │
│  │  GET /health   → health JSON     │    │
│  │  POST /generate → TTS engine     │    │
│  │  GET /*        → SPA catch-all   │    │
│  └──────────────────────────────────┘    │
│                                          │
│  /app/frontend/  ← built web/ React app  │
│  /app/backend/   ← Python FastAPI code   │
│  /app/data/      ← DB, profiles, cache   │
└──────────────────────────────────────────┘
         │
         ├── 127.0.0.1:17493 (localhost only)
         └── ./output/ (bind-mount for audio)

Build Details

The Dockerfile uses a 3-stage build to keep the runtime image small:

  1. frontend stage (oven/bun:1): Strips tauri/landing workspaces from package.json, installs deps, runs vite build (skipping tsc due to pre-existing upstream type errors)
  2. backend-builder stage (python:3.11-slim): Installs Python deps including Qwen3-TTS into a prefix
  3. Runtime stage (python:3.11-slim): Copies built frontend + Python packages, runs as non-root voicebox user with health check

Security

  • Port bound to 127.0.0.1 only (not accessible from network)
  • Runs as non-root user inside container
  • Resource limits (4 CPUs, 8GB RAM) via deploy config
  • No changes to existing authentication or CORS behavior

Notes

  • The web/ build skips TypeScript checking (tsc) because there are pre-existing type errors in the shared app/ source. Vite handles the build fine without strict type checking. These errors should be fixed upstream separately.
  • The frontend static file serving is conditionally activated — it only runs when /app/frontend/ exists (i.e., inside the Docker container). Running the backend outside Docker behaves exactly as before.
  • Models are auto-downloaded from HuggingFace on first generation. The huggingface-cache named volume ensures they persist across container rebuilds.

Testing

  • docker compose build succeeds
  • docker compose up -d starts container
  • curl http://localhost:17493/health returns healthy status
  • Web UI loads at http://localhost:17493
  • API endpoints remain functional
  • Running backend outside Docker is unaffected (no /app/frontend/ → JSON response at /)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant