feat: add Docker + web deployment support#161
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds Docker containerization: a comprehensive .dockerignore, a multi-stage Dockerfile (frontend build, Python deps, runtime image), and a docker-compose.yml to define the voicebox service, volumes, network, port binding, and resource limits. Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Build as Docker Build
participant Front as Frontend Stage
participant Back as Backend-builder Stage
participant Runtime as Runtime Image
participant Compose as docker-compose
participant Client as Client
Dev->>Build: docker build (context)
Build->>Front: run bun/Vite build (frontend assets)
Build->>Back: install Python deps into /install
Front-->>Runtime: copy built frontend assets
Back-->>Runtime: copy preinstalled Python packages and backend code
Build-->>Runtime: finalize image (non-root, healthcheck, expose 17493)
Dev->>Compose: docker-compose up (uses built image)
Compose->>Runtime: start container with volumes, env, networks, limits
Client->>Runtime: HTTP requests to 127.0.0.1:17493
Runtime-->>Client: API responses
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docker-compose.yml`:
- Around line 23-25: TTS_MODE is defined in docker-compose but not read by the
app; either remove TTS_MODE from docker-compose/docs or add code to consume it
by reading os.getenv("TTS_MODE") (or via the existing config loader / Settings
class) and wiring it into the TTS provider selection code (e.g., where the app
chooses between local vs remote TTS in the TTSManager/initialize_tts function)
so the environment variable controls provider choice; update tests/docs
accordingly.
- Around line 11-16: The bind-mount ./output:/app/data/generations can become
root-owned on the host and block the container user voicebox from writing; fix
by either (A) switching to a named volume instead of the host bind-mount so
Docker manages ownership, or (B) ensuring the host directory ./output is
pre-created and chowned to the same UID/GID as the voicebox user before starting
the container (or add an init step/entrypoint that creates /app/data/generations
and chowns it to voicebox at runtime). Reference the mount ./output, container
path /app/data/generations, and the voicebox user when implementing the change
or documenting the prerequisite.
In `@Dockerfile`:
- Around line 36-37: Pin the GitHub dependencies to immutable commit SHAs to
make builds reproducible: replace the moving refs by appending a specific commit
SHA to each URL—update the Dockerfile RUN line installing
git+https://github.com/QwenLM/Qwen3-TTS.git to use
git+https://github.com/QwenLM/Qwen3-TTS.git@<commit-sha>, and update the backend
requirements entries for the packages declared as linacodec @
git+https://github.com/ysharma3501/LinaCodec.git and Zipvoice @
git+https://github.com/ysharma3501/LuxTTS.git to linacodec @
git+https://github.com/ysharma3501/LinaCodec.git@<commit-sha> and Zipvoice @
git+https://github.com/ysharma3501/LuxTTS.git@<commit-sha> respectively (choose
the appropriate commit SHAs and test that pip installs still succeed).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 179b3e52-0f1b-491b-b90c-360e645c393e
📒 Files selected for processing (3)
.dockerignoreDockerfiledocker-compose.yml
| volumes: | ||
| # Bind-mount for generated audio (customize the host path as needed) | ||
| # Host side: ./output/ | ||
| # Container side: /app/data/generations/ | ||
| - ./output:/app/data/generations | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify mount + host directory writeability prerequisites.
set -e
echo "Checking compose mount definition..."
rg -n '^\s*-\s*\./output:/app/data/generations' docker-compose.yml
echo "Checking explicit user override in compose..."
rg -n '^\s*user:\s*' docker-compose.yml || true
echo "Checking host output directory permissions (if present)..."
if [ -d output ]; then
stat -c '%u:%g %a %n' output
[ -w output ] && echo "output is writable by current host user" || echo "output is NOT writable by current host user"
else
echo "output directory is missing (Docker may create it with root ownership depending on host setup)."
fiRepository: jamiepine/voicebox
Length of output: 341
🏁 Script executed:
git ls-files | grep -i dockerfileRepository: jamiepine/voicebox
Length of output: 72
🏁 Script executed:
cat -n DockerfileRepository: jamiepine/voicebox
Length of output: 2957
🏁 Script executed:
cat -n docker-compose.ymlRepository: jamiepine/voicebox
Length of output: 1277
Fix bind-mount write permissions for generated audio output.
The container runs as non-root user voicebox (Dockerfile lines 44–69), and /app/data/generations is created with voicebox ownership (line 65–66). However, the bind-mount ./output:/app/data/generations (line 15) overlays the host directory, which Docker will create as root-owned if it doesn't exist. This will cause write failures when the voicebox user attempts to save generated audio.
Ensure ./output is pre-created with permissions that allow the container user to write, or document this prerequisite for users.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docker-compose.yml` around lines 11 - 16, The bind-mount
./output:/app/data/generations can become root-owned on the host and block the
container user voicebox from writing; fix by either (A) switching to a named
volume instead of the host bind-mount so Docker manages ownership, or (B)
ensuring the host directory ./output is pre-created and chowned to the same
UID/GID as the voicebox user before starting the container (or add an init
step/entrypoint that creates /app/data/generations and chowns it to voicebox at
runtime). Reference the mount ./output, container path /app/data/generations,
and the voicebox user when implementing the change or documenting the
prerequisite.
| RUN pip install --no-cache-dir --prefix=/install \ | ||
| git+https://github.com/QwenLM/Qwen3-TTS.git |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether GitHub git+ dependencies are pinned to immutable refs.
python - <<'PY'
import pathlib, re
paths = ["Dockerfile", "backend/requirements.txt"]
pat = re.compile(r'git\+https://github\.com/\S+')
for p in paths:
txt = pathlib.Path(p).read_text()
for i, line in enumerate(txt.splitlines(), 1):
if "git+https://github.com/" in line:
pinned = "@" in line.split("git+https://github.com/",1)[1]
status = "PINNED" if pinned else "UNPINNED"
print(f"{p}:{i}: {status}: {line.strip()}")
PYRepository: jamiepine/voicebox
Length of output: 326
Pin GitHub dependencies to immutable refs across the repository.
Line 37 in the Dockerfile and lines 21-22 in backend/requirements.txt install from moving branches/HEAD, which makes builds non-reproducible and weakens supply-chain guarantees.
All three instances require pinning to a commit SHA:
Dockerfile:37:git+https://github.com/QwenLM/Qwen3-TTS.gitbackend/requirements.txt:21:linacodec @ git+https://github.com/ysharma3501/LinaCodec.gitbackend/requirements.txt:22:Zipvoice @ git+https://github.com/ysharma3501/LuxTTS.git
🔧 Proposed fix example (Dockerfile)
+ARG QWEN3_TTS_REF=<<commit-sha>>
RUN pip install --no-cache-dir --prefix=/install \
- git+https://github.com/QwenLM/Qwen3-TTS.git
+ git+https://github.com/QwenLM/Qwen3-TTS.git@${QWEN3_TTS_REF}For requirements.txt, append @<commit-sha> to each URL.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` around lines 36 - 37, Pin the GitHub dependencies to immutable
commit SHAs to make builds reproducible: replace the moving refs by appending a
specific commit SHA to each URL—update the Dockerfile RUN line installing
git+https://github.com/QwenLM/Qwen3-TTS.git to use
git+https://github.com/QwenLM/Qwen3-TTS.git@<commit-sha>, and update the backend
requirements entries for the packages declared as linacodec @
git+https://github.com/ysharma3501/LinaCodec.git and Zipvoice @
git+https://github.com/ysharma3501/LuxTTS.git to linacodec @
git+https://github.com/ysharma3501/LinaCodec.git@<commit-sha> and Zipvoice @
git+https://github.com/ysharma3501/LuxTTS.git@<commit-sha> respectively (choose
the appropriate commit SHAs and test that pip installs still succeed).
TTS_MODE is not read by any code in the backend — it only exists in unimplemented planning docs. Remove it to avoid confusing users.
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 upworkflow 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
Dockerfileweb/frontend, (2) Python deps compiled in builder, (3) slim runtime image with both frontend + backenddocker-compose.yml.dockerignoretauri/,landing/,docs/) and build artifacts for a lean imageModified Files
backend/main.pyGET /endpoint now servesindex.htmlwhen 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
Generated audio files appear in the
./output/directory (configurable via the bind-mount indocker-compose.yml).Architecture
Build Details
The Dockerfile uses a 3-stage build to keep the runtime image small:
frontendstage (oven/bun:1): Stripstauri/landingworkspaces frompackage.json, installs deps, runsvite build(skippingtscdue to pre-existing upstream type errors)backend-builderstage (python:3.11-slim): Installs Python deps including Qwen3-TTS into a prefixpython:3.11-slim): Copies built frontend + Python packages, runs as non-rootvoiceboxuser with health checkSecurity
127.0.0.1only (not accessible from network)Notes
web/build skips TypeScript checking (tsc) because there are pre-existing type errors in the sharedapp/source. Vite handles the build fine without strict type checking. These errors should be fixed upstream separately./app/frontend/exists (i.e., inside the Docker container). Running the backend outside Docker behaves exactly as before.huggingface-cachenamed volume ensures they persist across container rebuilds.Testing
docker compose buildsucceedsdocker compose up -dstarts containercurl http://localhost:17493/healthreturns healthy statushttp://localhost:17493/app/frontend/→ JSON response at/)Summary by CodeRabbit