VoiceStudio (formerly OmniVoice Studio) — an open-source, fully local ElevenLabs alternative: voice cloning, voice design, dubbing, dictation, transcription and audiobook creation. This Helm chart runs the headless server image (API + web UI on a single port), CPU by default with optional NVIDIA or AMD ROCm acceleration, driven from values.yaml via the bjw-s common library.
helm repo add obeone https://charts.obeone.cloud
helm repo update
helm install voicestudio obeone/voicestudio
VoiceStudio (formerly OmniVoice Studio) is an open-source, fully local alternative to ElevenLabs: voice cloning, voice design, video dubbing, dictation, transcription and audiobook creation. This chart runs its headless server image, where one port serves both the API and the web UI, on CPU by default with optional NVIDIA or AMD ROCm acceleration.
ghcr.io/debpalash/voicestudiocharts/voicestudiokubeVersion constraint (see Chart.yaml)linux/amd64 nodes: upstream publishes no arm64 imageThis chart is built on the bjw-s-labs common library.
Most configuration keys (controllers, service, ingress, persistence,
…) follow its schema; see the common library documentation
for everything it supports beyond what is spelled out in values.yaml.
Defaults are meant to work out of the box on any cluster. The full list of
options lives in values.yaml, is validated by
values.schema.json at install time, and is documented
in the Values section below. Override it with your own values file:
helm install voicestudio obeone/voicestudio -f my-values.yaml
VoiceStudio requires an administrator API key, and the web UI asks for it on
first use. The pod reads it from the Secret <fullname>-api-key (key api-key)
and waits until it exists. Create it before or right after installing:
kubectl create secret generic voicestudio-api-key \
--from-literal=api-key="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
Alternatively set secrets.api-key.enabled=true and pass the value with
--set-string secrets.api-key.stringData.api-key=....
The default image is the CUDA build, which falls back to CPU when no GPU is
visible. For an NVIDIA GPU, request it in both requests and limits, and set
the RuntimeClass if your cluster needs one:
defaultPodOptions:
runtimeClassName: nvidia
controllers:
main:
containers:
main:
resources:
requests:
nvidia.com/gpu: 1
limits:
nvidia.com/gpu: 1
For an AMD GPU, switch to the ROCm image (image.tag: "0.5.6-rocm")
and request amd.com/gpu through the AMD device plugin.
The image carries a single PyTorch layer of about 4 GB. If the pull fails with
unexpected commit digest (the download was cut short), switch to the Docker
Hub mirror, which publishes the same image with identical tags:
controllers:
main:
containers:
main:
image:
repository: docker.io/palashdeb/omnivoice-studio
helm repo update
helm upgrade voicestudio obeone/voicestudio
Each release lists its changes in the Artifact Hub changelog; give it a look before jumping across several chart versions.
helm uninstall voicestudio
PersistentVolumeClaims created by the chart are kept around: delete them manually if you also want the data gone.
Kubernetes: >=1.31.0-0
| Repository | Name | Version |
|---|---|---|
| https://bjw-s-labs.github.io/helm-charts | common | 5.2.1 |
| Key | Type | Default | Description |
|---|---|---|---|
| controllers.main.containers.main.env | object | {"HF_HOME":"/app/omnivoice_data/huggingface","OMNIVOICE_API_KEY":{"valueFrom":{"secretKeyRef":{"key":"api-key","name":"-api-key"}}},"OMNIVOICE_BIND_HOST":"0.0.0.0","OMNIVOICE_DATA_DIR":"/app/omnivoice_data","OMNIVOICE_SERVER_MODE":"1","PYTHONUNBUFFERED":"1"} |
Environment of the VoiceStudio backend. |
| controllers.main.containers.main.env.OMNIVOICE_API_KEY | object | {"valueFrom":{"secretKeyRef":{"key":"api-key","name":"-api-key"}}} |
Administrator API key, REQUIRED: settings, diagnostics and other admin actions are refused without it, and the web UI asks for it (the upstream Compose file will not start without one). Read from the <fullname>-api-key Secret, which you either create yourself (see NOTES) or let the chart create via secrets.api-key below. The pod stays in CreateContainerConfigError until it exists. |
| controllers.main.containers.main.image.pullPolicy | string | "IfNotPresent" |
|
| controllers.main.containers.main.image.repository | string | "ghcr.io/debpalash/voicestudio" |
Container image. Mirrored on Docker Hub as palashdeb/omnivoice-studio with identical tags. |
| controllers.main.containers.main.image.tag | string | "" |
Image tag. Defaults to the chart’s appVersion (CUDA build, which also runs CPU-only when no NVIDIA GPU is present). For AMD GPUs use the ROCm variant, e.g. “0.5.6-rocm”. |
| controllers.main.containers.main.probes.liveness.custom | bool | true |
|
| controllers.main.containers.main.probes.liveness.enabled | bool | true |
|
| controllers.main.containers.main.probes.liveness.spec.failureThreshold | int | 5 |
|
| controllers.main.containers.main.probes.liveness.spec.httpGet.path | string | "/health" |
|
| controllers.main.containers.main.probes.liveness.spec.httpGet.port | int | 3900 |
|
| controllers.main.containers.main.probes.liveness.spec.periodSeconds | int | 30 |
|
| controllers.main.containers.main.probes.liveness.spec.timeoutSeconds | int | 10 |
|
| controllers.main.containers.main.probes.readiness.custom | bool | true |
|
| controllers.main.containers.main.probes.readiness.enabled | bool | true |
|
| controllers.main.containers.main.probes.readiness.spec.failureThreshold | int | 3 |
|
| controllers.main.containers.main.probes.readiness.spec.httpGet.path | string | "/health" |
|
| controllers.main.containers.main.probes.readiness.spec.httpGet.port | int | 3900 |
|
| controllers.main.containers.main.probes.readiness.spec.periodSeconds | int | 10 |
|
| controllers.main.containers.main.probes.readiness.spec.timeoutSeconds | int | 5 |
|
| controllers.main.containers.main.probes.startup.custom | bool | true |
|
| controllers.main.containers.main.probes.startup.enabled | bool | true |
|
| controllers.main.containers.main.probes.startup.spec.failureThreshold | int | 90 |
|
| controllers.main.containers.main.probes.startup.spec.httpGet.path | string | "/health" |
|
| controllers.main.containers.main.probes.startup.spec.httpGet.port | int | 3900 |
|
| controllers.main.containers.main.probes.startup.spec.periodSeconds | int | 10 |
|
| controllers.main.containers.main.probes.startup.spec.timeoutSeconds | int | 5 |
|
| controllers.main.containers.main.resources | object | {"limits":{"memory":"8Gi"},"requests":{"cpu":"500m","memory":"2Gi"}} |
Resource envelope. Speech models are memory-hungry; no CPU limit so inference can burst. To use an NVIDIA GPU, add nvidia.com/gpu: 1 to BOTH requests and limits (and set runtimeClassName if your cluster needs it). For AMD, request amd.com/gpu: 1 with the ROCm image. |
| controllers.main.containers.main.securityContext.allowPrivilegeEscalation | bool | false |
|
| controllers.main.containers.main.securityContext.capabilities.drop[0] | string | "ALL" |
|
| controllers.main.pod.labels | object | {} |
Extra pod labels. Example for Sablier scale-to-zero on a shared GPU: |
| controllers.main.strategy | string | "Recreate" |
|
| controllers.main.type | string | "deployment" |
|
| defaultPodOptions | object | {"automountServiceAccountToken":false,"nodeSelector":{"kubernetes.io/arch":"amd64"},"runtimeClassName":"","securityContext":{"seccompProfile":{"type":"RuntimeDefault"}}} |
Pod-wide options applied to every controller in this chart. |
| defaultPodOptions.nodeSelector | object | {"kubernetes.io/arch":"amd64"} |
Node selection. Upstream only publishes linux/amd64 images (CUDA and ROCm alike), so the pod is pinned to amd64 nodes. Add a GPU host label here when you enable acceleration. |
| defaultPodOptions.runtimeClassName | string | "" |
RuntimeClass exposing the GPU to the pod (e.g. “nvidia”). Leave empty for CPU-only clusters, or for AMD GPUs exposed through a device plugin. |
| ingress | object | {"main":{"annotations":{},"className":"","enabled":false,"hosts":[{"host":"chart-example.local","paths":[{"path":"/","pathType":"Prefix","service":{"identifier":"main","port":"http"}}]}],"tls":[{"hosts":["chart-example.local"],"secretName":"tls-chart-example-local"}]}} |
Ingress. Disabled by default; flip enabled and set a real host to expose the UI. The backend speaks plain HTTP and the admin key travels with requests, so terminate TLS at the ingress and never expose it unencrypted. |
| persistence | object | {"data":{"accessMode":"ReadWriteOnce","enabled":true,"globalMounts":[{"path":"/app/omnivoice_data"}],"size":"20Gi","type":"persistentVolumeClaim"},"dshm":{"globalMounts":[{"path":"/dev/shm"}],"medium":"Memory","sizeLimit":"1Gi","type":"emptyDir"}} |
Storage. |
| persistence.data | object | {"accessMode":"ReadWriteOnce","enabled":true,"globalMounts":[{"path":"/app/omnivoice_data"}],"size":"20Gi","type":"persistentVolumeClaim"} |
Everything VoiceStudio keeps: SQLite database, user voices, settings and the Hugging Face model cache (~4 GB on first run, more as you add engines). |
| persistence.data.globalMounts | list | [{"path":"/app/omnivoice_data"}] |
Reuse a pre-created PVC instead. existingClaim: “” |
| route | object | {"main":{"enabled":false,"hostnames":["chart-example.local"],"kind":"HTTPRoute","parentRefs":[{"name":"gateway","namespace":"gateway-system"}],"rules":[{"backendRefs":[{"identifier":"main","port":3900}],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}} |
Gateway API HTTPRoute, mirroring the Ingress above. Disabled by default: pick either Ingress or HTTPRoute, not both. Requires the Gateway API CRDs and an existing Gateway in the cluster. |
| route.main.enabled | bool | false |
Enable the HTTPRoute. Mutually exclusive with ingress.main.enabled. |
| route.main.hostnames | list | ["chart-example.local"] |
Hostnames served by this route. |
| route.main.kind | string | "HTTPRoute" |
Route kind. HTTPRoute, GRPCRoute, TCPRoute, TLSRoute or UDPRoute. |
| route.main.parentRefs | list | [{"name":"gateway","namespace":"gateway-system"}] |
Gateways this route attaches to. |
| route.main.rules | list | [{"backendRefs":[{"identifier":"main","port":3900}],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}] |
Routing rules. identifier refers to a Service defined above. |
| secrets | object | {"api-key":{"enabled":false,"stringData":{"api-key":""},"suffix":"api-key"}} |
Secrets managed by the chart. |
| secrets.api-key | object | {"enabled":false,"stringData":{"api-key":""},"suffix":"api-key"} |
Chart-managed administrator key, rendered as <fullname>-api-key. Disabled by default so the key never lands in a values file by accident: create the Secret out of band (see NOTES). If you enable it, pass the value at install time (--set-string secrets.api-key.stringData.api-key=...). |
| service | object | {"main":{"controller":"main","ports":{"http":{"port":3900,"protocol":"TCP","targetPort":3900},"worker":{"enabled":false,"port":7443,"protocol":"TCP","targetPort":7443}},"type":"ClusterIP"}} |
Service exposing the API and web UI inside the cluster. |
| service.main.ports.worker | object | {"enabled":false,"port":7443,"protocol":"TCP","targetPort":7443} |
TLS control plane for remote GPU workers. Disabled by default; enable it together with OMNIVOICE_WORKER_PORT only if you enroll workers. |
Charts in this repository are signed with GPG and every release ships a
provenance file. The public key is available at
charts.obeone.cloud/public_key.gpg,
fingerprint B9FE852F28888D27F8C9A11CD33E04CD22E335CE.
# Import the signing key into a legacy keyring (helm verifies with GnuPG v1 keyrings)
curl -fsSL https://charts.obeone.cloud/public_key.gpg | gpg --import
gpg --export > ~/.gnupg/pubring.gpg
# Pull the chart and check it against its provenance file
helm pull --verify obeone/voicestudio
This is a personal chart repository, maintained on a best-effort basis. Bug reports and contributions are welcome on GitHub.
Autogenerated from chart metadata using helm-docs v1.14.2