All apps · 0 apps
feedBack-Demucs-Server
Docker app from joeygatorhands' Repository
Overview
Readme
View on GitHubSlopsmith Demucs Server
A lightweight GPU-accelerated service providing AI source separation, lyrics alignment, and per-syllable pitch extraction for Slopsmith. Designed to run on a desktop with a CUDA GPU while Slopsmith runs on a NAS or Docker host.
Features
Source Separation (POST /separate)
Splits audio into individual stems using Demucs:
- Default model:
htdemucs_ft(4-stem fine-tuned: drums, bass, vocals, other) - Other models selectable per-request:
htdemucs_6s(6-stem incl. guitar/piano),mdx_extra(lighter) bs_roformer_sw— BS-Roformer-SW (6-stem: vocals/drums/bass/guitar/piano/other), via audio-separator. Higher SDR than Demucs (notably bass/guitar) with far less cross-stem bleed; checkpoint (~700 MB) lazy-downloads on first use to<cache>/_roformer-models/. Stems returned as lossless FLAC.- File upload or URL input
- Per-stem caching, keyed by audio and model (avoids re-processing; same song under two models caches separately)
- WebSocket progress updates
Lyrics Transcription (POST /transcribe)
Transcribe sung audio to word-level timed lyrics — no lyrics supplied. Whisper hears the words; wav2vec2 then force-aligns Whisper's own transcript, giving timestamps far tighter than Whisper's segment boundaries (a chart built from raw Whisper segments sits visibly late).
- Answers the question
/aligncan't: what are the lyrics, and when is each word sung? - Automatic language detection (or manual hint)
- An instrumental track transcribes to
{"segments": []}— that's an answer, not an error
Lyrics Alignment (POST /align)
Forced alignment of plain text lyrics against an audio file using WhisperX — Whisper transcription plus a wav2vec2 forced aligner for tighter sub-word timestamps:
- Line, word, syllable, or phoneme granularity
- Phoneme/character-level CTC alignment via wav2vec2 (per-language model)
- Syllable splitting layered on word output via pyphen hyphenation (CJK character support)
- Automatic language detection (or manual language hint)
- Used by the Lyrics Sync plugin and the Lyrics Karaoke plugin
Per-syllable Pitch Extraction (POST /pitch)
Estimates one MIDI note per syllable from a vocals stem using CREPE via torchcrepe. Powers the karaoke pitch chart in the Lyrics Karaoke plugin:
- CREPE neural pitch tracker — order-of-magnitude fewer octave errors than pYIN
- Confidence-weighted mode-of-semitone aggregation per syllable
- Song-wide range narrowing (clamps each syllable to ±12 semitones around the median)
- Octave-error correction against the song-wide median
- Neighbour-borrowed pitch for tokens CREPE can't lock (so whispered phrases still get bars)
Setup
Requirements
- Python 3.10+
- CUDA-capable GPU (recommended) or CPU fallback
- FFmpeg (
apt install ffmpeg/brew install ffmpeg)
Install (Native)
git clone https://github.com/got-feedback/feedback-demucs-server.git
cd feedback-demucs-server
python -m venv .venv
source .venv/bin/activate
# Step 1: Install main dependencies (fastapi, whisperx, torchcrepe, etc.)
# whisperx pins torch~=2.8.0 + torchaudio~=2.8.0
pip install -r requirements.txt
# Step 2: Install audio-separator SEPARATELY (diffq source-build workaround)
# Its deps pull in `diffq`, which has no wheel for Python 3.11+ and would try to
# compile from source. Its real deps are already in requirements.txt.
pip install "audio-separator>=0.44.0" --no-deps
# Step 3: Install demucs SEPARATELY (torchaudio version conflict workaround)
# demucs requires torchaudio<2.1, which conflicts with whisperx.
# Installing with --no-deps bypasses the bad pin.
# dora-search is demucs's logging lib (imported as `import dora`).
pip install demucs --no-deps
pip install einops julius lameenc openunmix pyyaml tqdm dora-search sphn
# Step 4 (optional): diffq, for QUANTIZED demucs checkpoints only.
# --only-binary=:all: makes pip fail rather than fall back to the sdist, so this can
# never start a compile.
#
# No `|| true` here on purpose: that would hide a network/index/permissions failure too,
# and you would think diffq was installed when it isn't. Let it fail loudly, and skip it
# ONLY if pip says "No matching distribution found" / "Could not find a version that
# satisfies" — that means no wheel exists for your Python (macOS on 3.11+, Linux on 3.13),
# which is safe: the bs_roformer_sw model feedBack splits with does not use diffq.
pip install "diffq-fixed>=0.2" --no-deps --only-binary=:all:
⚠️ Why the separate install steps?
demucs(PyPI 4.0.1) pinstorchaudio<2.1whilewhisperxneedstorchaudio~=2.8.0. These are incompatible. Installing demucs with--no-depsavoids the conflict. Demucs works fine with modern torchaudio — only thesave_audiofunction had issues, and that's patched inrun_demucs.pyto usesoundfileinstead.
audio-separatordeclaresdiffq (>=0.2); sys_platform != "win32".diffqis a C-extension whose newest wheels stop at cp310 — so on any Python 3.11+ pip falls back to its sdist and needs a C compiler. That is why this is not inrequirements.txt:pip install -rresolves that file first, so the compile would fail before the--no-depsstep could run. (Windows escapes this by accident: it resolves todiffq-fixed, which does ship modern wheels.)audio-separator's real runtime deps are listed inrequirements.txtinstead, and it is installed on top with--no-deps.
Run
python server.py --port 7865
Options:
| Flag | Default | Description |
|---|---|---|
--port |
7865 | Port to listen on |
--host |
0.0.0.0 | Host to bind to |
--model |
htdemucs_ft | Demucs model (htdemucs_ft, htdemucs_6s, mdx_extra) |
--device |
auto | Force cpu or cuda |
--api-key |
— | API key for authentication |
--skip-warmup |
— | Skip startup model-weight prefetch |
Environment variables override CLI defaults: SLOPSMITH_DEMUCS_MODEL, SLOPSMITH_DEMUCS_DEVICE, SLOPSMITH_API_KEY.
First-start model weight download
On first start the server pre-downloads model weights (~1.5 GB for all three endpoints: htdemucs_ft, Whisper medium, CREPE full, English wav2vec2). Subsequent restarts use cached weights.
The download runs in a background thread, so /health is queryable immediately. Each library prints its own tqdm progress bar.
/health reports per-model status:
{
"status": "ok",
"warmup": {
"demucs": "ready",
"whisperx": "downloading",
"crepe": "pending",
"whisperx_aligners": { "en": "ready" }
}
}
States: pending → downloading → ready | failed: <reason> | skipped | evicted.
Pass --skip-warmup for environments without internet access.
Run as a systemd service
- Copy and edit the service file:
cp slopsmith-demucs.service ~/.config/systemd/user/
# Edit ~/.config/systemd/user/slopsmith-demucs.service
# Set User, ExecStart paths to match your setup
nano ~/.config/systemd/user/slopsmith-demucs.service
- Enable and start:
systemctl --user daemon-reload
systemctl --user enable slopsmith-demucs
systemctl --user start slopsmith-demucs
- Monitor:
journalctl --user -u slopsmith-demucs --follow
Docker
Build
docker build -t slopsmith-demucs-server .
Run (CPU)
docker run -p 7865:7865 slopsmith-demucs-server
Run (GPU)
Requires nvidia-container-toolkit:
docker run --gpus all -p 7865:7865 slopsmith-demucs-server
Docker Compose
# Pull from GHCR and run (CPU)
docker compose up -d
# GPU mode: uncomment `gpus: all` in the compose file (needs nvidia-container-toolkit;
# Linux or Windows/WSL2 only — macOS cannot pass a GPU through at all)
docker compose up -d
⚠️ Ran an image from before 2026-07-12? Delete the cache volume.
Early images created their model-cache volume owned by root, while the server runs as an unprivileged user (uid 10001). The container would start and then immediately die with
PermissionError: [Errno 13] Permission denied: '/app/cache/_roformer-models'...which
restart: unless-stoppedturns into a crash-loop.The image is fixed — but pulling the new image is not enough on its own. Docker sets a volume's ownership only when it first creates it, so a volume made by an older image stays root-owned forever and will keep crash-looping on a perfectly good image. Remove it:
docker compose down docker volume rm feedback-demucs-cache # or: slopsmith-demucs-server_demucs-cache docker compose up -dThe volume only holds cached model weights — deleting it costs you a re-download, nothing else.
Persistent model cache
Model weights are stored in /app/cache inside the container. The compose file maps this to a persistent volume so weights survive restarts:
docker compose down # cache preserved
docker compose down -v # cache deleted (if using named volume)
To use a custom host path instead of a named volume (e.g. for Portainer or to save space on a specific drive), replace the volume in docker-compose.yml:
volumes:
- /home/AI/slopsmith-demucs-cache:/app/cache
Then copy the existing cache to the new location:
# Find old volume path
docker volume inspect slopsmith-demucs-server_demucs-cache
# Copy to new location
sudo cp -a /var/lib/docker/volumes/slopsmith-demucs-server_demucs-cache/_data/. /home/AI/slopsmith-demucs-cache/
Cache environment variables (all redirect to /app/cache to prevent container root disk exhaustion):
| Variable | Purpose |
|---|---|
SLOPSMITH_DEMUCS_CACHE |
Server cache root |
HF_HOME |
HuggingFace model cache |
TORCH_HOME |
PyTorch hub cache |
HUGGINGFACE_HUB_CACHE |
HuggingFace hub downloads |
Auto-update
The container can automatically check for repository updates and restart. Disabled by default (safe for Portainer/deployments without .git access).
To enable:
- Uncomment the
.gitbind mount indocker-compose.yml - Set
AUTO_UPDATE=truein environment - Redeploy
How it works:
- A background daemon runs inside the container
- Every
UPDATE_CHECK_INTERVALseconds (default: 3600 = 1 hour), it checks if the current time matchesUPDATE_TIME(default: 04:00) - At the configured time, it runs
git fetch originand comparesHEADwith@{upstream} - If changes are detected, it pulls the new code, reinstalls dependencies, and gracefully restarts the server
Configuration via environment variables:
| Variable | Default | Description |
|---|---|---|
AUTO_UPDATE |
false |
Enable/disable auto-update |
UPDATE_TIME |
04:00 |
Time of day to check (HH:MM, 24h) |
UPDATE_CHECK_INTERVAL |
3600 |
Seconds between time checks (3600 = 1 hour) |
SKIP_WARMUP |
false |
Skip model weight download on startup |
SLOPSMITH_DEMUCS_MODEL |
— | Override default Demucs model |
SLOPSMITH_API_KEY |
— | API authentication key |
CACHE_TTL |
24h |
Cache cleanup TTL (1h, 12h, 24h, or NEVER to disable auto-cleanup) |
Disable auto-update (default — safe for Portainer):
docker run -e AUTO_UPDATE=false -p 7865:7865 slopsmith-demucs-server
Cache cleanup
The server automatically deletes old stem cache directories to prevent disk growth. A background thread runs every 10 minutes, checks each stem cache directory under SLOPSMITH_DEMUCS_CACHE, and removes directories older than CACHE_TTL.
Model weight caches (torch/, huggingface/, locale/) are never deleted — only the stem output cache is cleaned.
| Variable | Default | Description |
|---|---|---|
CACHE_TTL |
24h |
Maximum age of cache entries (1h, 12h, 24h, or NEVER to disable) |
Disable auto-cleanup:
docker run -e CACHE_TTL=NEVER -p 7865:7865 slopsmith-demucs-server
Set custom TTL (e.g. 12 hours):
docker run -e CACHE_TTL=12h -p 7865:7865 slopsmith-demucs-server
GitHub Container Registry (CI)
The CI workflow (.github/workflows/docker-build.yml) automatically builds the Docker image, pushes it to GHCR, generates an SBOM, and runs a grype vulnerability scan on every push to main.
To enable on your fork:
- Go to your fork on GitHub → Actions tab
- Click "I understand my workflows, go ahead and enable them"
- Push to
main— the CI builds and scans automatically
Pull the latest image:
docker pull ghcr.io/YOUR_GITHUB_USER/slopsmith-demucs-server:latest
Or from the upstream repo (once PR is merged):
docker pull ghcr.io/byrongamatos/slopsmith-demucs-server:latest
Build directly from git (no clone needed):
# From upstream main
docker build -t slopsmith-demucs-server https://github.com/byrongamatos/slopsmith-demucs-server.git#main
# From your fork
docker build -t slopsmith-demucs-server https://github.com/YOUR_USER/slopsmith-demucs-server.git#main
# Run it
docker run --gpus all -p 7865:7865 slopsmith-demucs-server
Run via Docker Compose with git build:
services:
slopsmith-demucs:
build: https://github.com/byrongamatos/slopsmith-demucs-server.git#main
ports:
- "7865:7865"
API
GET /health
Returns server status, model, GPU availability, cache directory, and per-model warmup state (see First-start model weight download).
POST /separate
Separate audio into stems.
| Parameter | Type | Description |
|---|---|---|
file |
Upload | Audio file |
stems |
Query | Comma-separated stem names (default: drums,bass,vocals,other) |
model |
Query | Override model (optional) |
POST /transcribe
Transcribe sung audio to word-level timed lyrics using WhisperX (faster-whisper ASR + wav2vec2 forced alignment of its own transcript). Use this when you do not have the lyrics; use /align when you do.
| Parameter | Type | Description |
|---|---|---|
file |
Form (file) | Audio file (vocals stem) |
language |
Form | ISO 639-1/2 language code hint, e.g. en, es, pt (optional, auto-detected). Case-insensitive — the value is trimmed and lowercased before validation, so EN and en both work. Must then be 2–8 letters; subtags like en-US are rejected (400) because the hyphen is not a letter. |
Returns native WhisperX alignment output:
{"segments": [{"start": 12.3, "end": 15.1, "text": "hello world",
"words": [{"word": "hello", "start": 12.3, "end": 12.8, "score": 0.94}]}],
"language": "en"}
A stem with no singing in it returns {"segments": [], "language": "en"} with a 200 — an instrumental is a valid answer, not a failed request.
POST /align
Forced-align lyrics against audio using WhisperX (faster-whisper transcription + wav2vec2 forced aligner).
| Parameter | Type | Description |
|---|---|---|
file |
Form (file) | Audio file (vocals stem) |
text |
Form | Plain text lyrics |
language |
Form | ISO 639-1/2 language code hint, e.g. en, es, pt (optional, auto-detected). Case-insensitive — the value is trimmed and lowercased before validation, so EN and en both work. Must then be 2–8 letters; subtags like en-US are rejected (400) because the hyphen is not a letter. |
granularity |
Form | line (default), word, syllable, or phoneme |
Granularity behaviour:
line— segment-level boundaries.word— wav2vec2-aligned word timestamps. The first entry in each line carriesnew_line: true.syllable—wordoutput split via pyphen; carriesnew_lineon the first syllable of each line.phoneme— character-level CTC token timestamps from the aligner. Each entry carriesphoneme: true. With wav2vec2 character models these are letter-aligned; with phoneme-trained models they're true phonemes.
Returns: {"segments": [...], "language": "en"} where each segment is {start, end, text, ...}.
POST /pitch
Per-syllable pitch extraction using CREPE.
| Parameter | Type | Description |
|---|---|---|
file |
Form (file) | Vocals stem (any format librosa can read) |
lyrics |
Form | JSON array of {"t": float, "d": float} — token start / duration in seconds |
Returns: {"notes": [{"t": 12.34, "d": 0.5, "midi": 64}, ...]}. Tokens for which no pitch could be estimated (even after neighbour-borrow) are omitted.
GET /download/{job_id}/{stem}
Download a separated stem by job ID.
GET /jobs / GET /jobs/{job_id}
List or inspect separation jobs.
WS /ws/jobs/{job_id}
WebSocket for real-time separation progress updates.
Configure in Slopsmith
Set the Demucs Server URL to http://<your-server-ip>:7865 in Slopsmith settings.
License
AGPL-3.0-only — the same license as the feedBack app this server exists to serve, and as the plugins that call it. See LICENSE.
Install feedBack-Demucs-Server on Unraid in a few clicks.
Find feedBack-Demucs-Server in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/got-feedback/feedback-demucs-server:latestRuntime arguments
- Web UI
http://[IP]:[PORT:7865]- Network
bridge- Privileged
- false
- Extra Params
--user 0:0
Template configuration
The port for the Demucs Server REST API (defaulted to 8020)
- Target
- 7865
- Default
- 8020
- Value
- 8020
Location for downloaded model weights (Demucs, Whisper, Crepe) and transient audio caches.
- Target
- /app/cache
- Default
- /mnt/user/appdata/feedback-demucs-server
- Value
- /mnt/user/appdata/feedback-demucs-server
The default Demucs separation model (e.g., htdemucs_ft, htdemucs, htdemucs_6s).
- Target
- SLOPSMITH_DEMUCS_MODEL
- Default
- htdemucs_ft
- Value
- htdemucs_ft
Optional: The device PyTorch should use (e.g., cuda, cpu). Leave blank to auto-detect Nvidia GPU.
- Target
- SLOPSMITH_DEMUCS_DEVICE
Optional: API authentication key to secure the Demucs server endpoints.
- Target
- SLOPSMITH_API_KEY
Maximum age of transient audio/stems cache folders before deletion (e.g., 1h, 12h, 24h, or NEVER to disable auto-cleanup).
- Target
- CACHE_TTL
- Default
- 24h
- Value
- 24h
Set to true to skip downloading AI model weights on container startup. (Weights will instead download on-demand on the first request).
- Target
- SKIP_WARMUP
- Default
- false
- Value
- false
Internal application cache root directory.
- Target
- SLOPSMITH_DEMUCS_CACHE
- Default
- /app/cache
- Value
- /app/cache
Redirects HuggingFace models to the persistent cache directory.
- Target
- HF_HOME
- Default
- /app/cache/huggingface
- Value
- /app/cache/huggingface
Redirects PyTorch models to the persistent cache directory.
- Target
- TORCH_HOME
- Default
- /app/cache/torch
- Value
- /app/cache/torch
Redirects HuggingFace hub downloads to the persistent cache directory.
- Target
- HUGGINGFACE_HUB_CACHE
- Default
- /app/cache/huggingface/hub
- Value
- /app/cache/huggingface/hub
Enables auto-updates of server files at the configured update time (Requires .git mount).
- Target
- AUTO_UPDATE
- Default
- false
- Value
- false
Daily time check for updates (HH:MM format).
- Target
- UPDATE_TIME
- Default
- 04:00
- Value
- 04:00
Seconds between daily time check sweeps.
- Target
- UPDATE_CHECK_INTERVAL
- Default
- 3600
- Value
- 3600
Internal port the Demucs server listens to inside the container.
- Target
- PORT
- Default
- 7865
- Value
- 7865