Files
VoiceStudio/docs/install/docker.md
T
99e01610bb feat(docker): publish ROCm/AMD GPU image variant (#1165) (#1166)
The Docker image was CUDA-only, so AMD GPUs (e.g. RX 7900 XTX under
Podman) silently ran on CPU. Every preview and release now also ships a
ROCm variant built from the same Dockerfile:

- deploy/Dockerfile: parameterize the runtime base with a BASE_IMAGE
  build-arg (default unchanged: pytorch/pytorch 2.8.0 CUDA). Add
  PIP/UV_BREAK_SYSTEM_PACKAGES for the ROCm base's PEP-668-marked
  Ubuntu 24.04 Python (no-op on the conda CUDA base), and a build-time
  GPU_FLAVOR guard asserting the dependency install did not clobber the
  base image's GPU torch/torchaudio — a future dep bump that forces a
  torch reinstall now fails the build instead of shipping a CPU-only
  "ROCm" image.
- .github/workflows/docker.yml: new build-and-push-rocm job (separate
  job for runner disk — the ROCm base is ~25 GB unpacked, so it frees
  the preinstalled toolchains first). Tags mirror the CUDA semantics
  with a -rocm suffix (:rocm rolling preview, :stable-rocm, :X.Y.Z-rocm,
  :X.Y-rocm, :sha-xxxx-rocm) on both GHCR and Docker Hub, same secret
  gating. flavor latest=false so release tags can't clobber :latest.
  No cache-to: the ROCm layers would blow the 10 GB GHA cache budget.
- deploy/docker-compose.yml: new opt-in 'rocm' profile passing the GPU
  through via /dev/kfd + /dev/dri, with HSA_OVERRIDE_GFX_VERSION=11.0.0
  documented (user-set, not baked in — backend auto-sets it for known
  consumer GFX IDs).
- Docs-sync: docker.md (ROCm quick start incl. Podman/Quadlet, tag
  table, troubleshooting), dockerhub-overview.md, README AMD note,
  linux.md ROCm section cross-link, CHANGELOG [Unreleased].

Base image: rocm/pytorch:rocm7.2.4_ubuntu24.04_py3.12_pytorch_release_2.8.0
— torch 2.8.0 exactly matches the CUDA image (identical resolution, so
uv keeps it), py3.12 satisfies requires-python >=3.11 (the ubuntu22.04
variants are py3.10 and do not).

Closes #1165

Co-authored-by: mergetest <nizam4103@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 15:34:28 +05:30

9.2 KiB

OmniVoice Studio — Install with Docker

For headless servers, dedicated GPUs, or "I want one command" deployments. The docker image bundles the backend; the UI is served over HTTP and you open it in a normal browser.

Official images: ghcr.io/debpalash/omnivoice-studio and palashdeb/omnivoice-studio on Docker Hub — same images, same tags.

Image ↔ version mapping

Tag What you get
:latest Rolling preview — latest commit on main (always one patch ahead of the last release). This is the preview channel; pin :stable for production.
:stable Most recent versioned release (updated on every v* git tag)
:0.3.17 Exact release version
:0.3 Latest patch within the 0.3 minor
:main Alias of the same rolling main build as :latest
:sha-xxxxxxx Specific commit (produced by manual workflow dispatch)
:rocm AMD GPU (ROCm) build of the rolling preview — the ROCm analogue of :latest
:stable-rocm, :0.3.17-rocm, :0.3-rocm, :sha-xxxxxxx-rocm ROCm builds of the corresponding CUDA tags above

Versioning rule: main always carries last release + 1 patch, so :latest (preview) version-sorts above :stable — upgrades flow naturally.

Note on the update-channel toggle: The update-channel UI (Settings → About → Update channel) is part of the Tauri desktop app's built-in auto-updater. It does not apply to the Docker image — the Docker image is the headless web-server build. To update your Docker deployment, pull the new image tag and recreate the container (docker compose pull && docker compose up -d).

Pull and run (CPU)

docker pull ghcr.io/debpalash/omnivoice-studio:latest

docker run -d --name omnivoice \
  -p 127.0.0.1:3900:3900 \
  -v omnivoice-data:/app/omnivoice_data \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  ghcr.io/debpalash/omnivoice-studio:latest

Docker Hub mirror: the same images are published to palashdeb/omnivoice-studio on Docker Hub with identical tags — swap the image for palashdeb/omnivoice-studio:latest if you prefer Docker Hub. Tag semantics (:latest = rolling main preview, :stable/:X.Y.Z = releases) are the same on both registries.

Open http://localhost:3900. The first run downloads ~2.4 GB of model weights — follow docker logs -f omnivoice to watch.

Pull and run (NVIDIA GPU)

docker run -d --name omnivoice --gpus all \
  -p 127.0.0.1:3900:3900 \
  -v omnivoice-data:/app/omnivoice_data \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  ghcr.io/debpalash/omnivoice-studio:latest

GPU mode requires the NVIDIA Container Toolkit on the host.

Pull and run (AMD GPU / ROCm)

AMD GPUs use the dedicated :rocm image variant — the default (CUDA) image runs CPU-only on AMD hardware. The ROCm userspace ships inside the image; the host only needs the amdgpu kernel driver. Pass the GPU through as plain device nodes (no container toolkit needed):

docker run -d --name omnivoice \
  --device /dev/kfd --device /dev/dri \
  -p 127.0.0.1:3900:3900 \
  -v omnivoice-data:/app/omnivoice_data \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  ghcr.io/debpalash/omnivoice-studio:rocm

The same flags work with Podman (podman run --device /dev/kfd --device /dev/dri …); in a Quadlet unit that's two AddDevice= lines:

# ~/.config/containers/systemd/omnivoice.container
[Container]
Image=ghcr.io/debpalash/omnivoice-studio:rocm
AddDevice=/dev/kfd
AddDevice=/dev/dri
PublishPort=127.0.0.1:3900:3900
Volume=omnivoice-data:/app/omnivoice_data

Release pins exist too: :stable-rocm, :0.3.22-rocm, :0.3-rocm mirror the CUDA tags exactly.

RDNA3 consumer cards (RX 7900 XTX/XT, gfx1100): the backend auto-sets HSA_OVERRIDE_GFX_VERSION for consumer GFX IDs missing from ROCm's official support matrix, so try without any override first. If the GPU still isn't detected, force it explicitly with -e HSA_OVERRIDE_GFX_VERSION=11.0.0 (user-set on the container — it is deliberately not baked into the image, because the right value depends on your card).

Verify the container sees the GPU:

docker exec omnivoice python3 -c \
  "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"

(ROCm-built PyTorch reports through torch.cuda.*True plus your card's name means GPU acceleration is active. The Settings → System panel shows the same device.)

# CPU
docker compose -f deploy/docker-compose.yml --profile cpu up -d

# NVIDIA GPU
docker compose -f deploy/docker-compose.yml --profile gpu up -d

# AMD GPU (ROCm)
docker compose -f deploy/docker-compose.yml --profile rocm up -d

The docker-compose.yml shipped in deploy/ defaults to 127.0.0.1:3900 on the host. The backend inside the container binds to 0.0.0.0 so the host port mapping can forward — the host-side 127.0.0.1 binding is what enforces loopback-only.

LAN access

To expose OmniVoice on your LAN (e.g. you're running it on a homelab box and opening the UI from a laptop), change the host port mapping:

# deploy/docker-compose.yml
services:
  omnivoice:
    ports:
      - "0.0.0.0:3900:3900"   # ← was 127.0.0.1:3900:3900

The OmniVoice frontend defaults to the same origin the page was served from, so opening the UI from http://<lan-ip>:3900 Just Works for both the page load and the API/media requests it makes afterwards.

If you front the app with a reverse proxy and the API and UI land on different origins, pin the API base explicitly. Use OMNIVOICE_PUBLIC_API_BASE — a runtime env var the backend injects into the page, so it works with the prebuilt image via docker run -e (the older VITE_OMNIVOICE_API is inlined at build time and cannot be set on a prebuilt image):

docker run -e OMNIVOICE_PUBLIC_API_BASE=https://api.your-host.example \
  -p 0.0.0.0:3900:3900 \
  ghcr.io/debpalash/omnivoice-studio:latest

OMNIVOICE_PUBLIC_API_BASE must be a plain http(s)://… URL; anything else is ignored and the app falls back to same-origin. If you build from source you may instead bake VITE_OMNIVOICE_API at build time, but the runtime var above is simpler and image-agnostic.

Security: OmniVoice ships no authentication. Anything on your LAN with the URL can use the app. Put it behind a reverse proxy with basic_auth (Caddy / nginx + htpasswd) or a private network overlay (Tailscale, ZeroTier) before exposing publicly.

Volume mounts

Two paths are worth persisting across container restarts:

Mount Purpose Why
omnivoice_data:/app/omnivoice_data Project DB, user voices, settings Survives upgrade; encrypted HF token lives here
~/.cache/huggingface:/root/.cache/huggingface HF model cache Re-using your host's cache saves ~2.4 GB of re-downloads

Troubleshooting

  • Container reports 0.2.7 but image is tagged 0.3.x: This was a workflow bug (fixes #249, #251) — the :latest tag was not being updated on release tag pushes. Pull the image again after the fix is merged: docker pull ghcr.io/debpalash/omnivoice-studio:latest. The running version is now shown in Settings → About → Version (read live from the backend), so the web UI no longer displays a dash in Docker.
  • Checking which version is running: docker exec omnivoice python -c "import importlib.metadata; print(importlib.metadata.version('omnivoice'))", or hit the /health endpoint — it returns {"status": "ok", "device": ..., "version": "0.3.x"}.
  • "Loopback origin required" errors (and a blank version): the desktop build restricts the /system/* and /api/settings/* routes to a loopback origin, but Docker's NAT makes every request look non-loopback, so the gate used to 403 the whole admin UI (issue #261). The image now ships with OMNIVOICE_SERVER_MODE=1, which relaxes that gate for the headless deployment — exposure is instead governed by your -p port mapping (keep the 127.0.0.1: prefix to stay local) plus the optional share PIN. If you front the container with your own auth proxy on loopback, set OMNIVOICE_SERVER_MODE=0 to re-enable the strict gate.
  • Media-preview 404 in LAN mode: see the LAN access section above — the window.location.host fix shipped in v0.3.
  • GPU not detected (NVIDIA): verify docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu22.04 nvidia-smi succeeds first.
  • GPU not detected (AMD): make sure you pulled the :rocm tag (the default image is CUDA-only) and passed --device /dev/kfd --device /dev/dri. Check the container sees the card with docker exec omnivoice rocminfo | grep -i gfx; on RDNA3 consumer cards try -e HSA_OVERRIDE_GFX_VERSION=11.0.0 — see Pull and run (AMD GPU / ROCm) above.
  • More entries: docs/install/troubleshooting.md.