Add Navidrome and SUB/WAVE AI radio station

Navidrome indexes the existing music library on nas_media for Subsonic
access. SUB/WAVE (byo-proxy variant, LAN-only) provides the Icecast
broadcast, Liquidsoap crossfade/ducking, and AI DJ controller, reusing
the existing Ollama instance over npm-network for the LLM and Piper
for TTS.
This commit is contained in:
2026-08-08 21:20:40 +00:00
parent d6c214d80d
commit fb728d4bfc
2 changed files with 163 additions and 0 deletions
+138
View File
@@ -0,0 +1,138 @@
# SUB/WAVE — self-hosted AI radio station.
# Deployed as the "byo-proxy" variant (no bundled Caddy): web/controller/
# broadcast bind host ports directly since this station is LAN-only for now.
# Upstream project: https://github.com/perminder-klair/subwave
#
# Values are hardcoded rather than relying on ${VAR} compose substitution,
# since Portainer git-linked stacks don't read this repo's local .env for
# that purpose (see deployment-procedures.md). ADMIN_USER/ADMIN_PASS still
# come from a real (gitignored) ./.env file next to this compose file, read
# directly by Docker's env_file mechanism.
x-logging: &default-logging
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
# -------------------------------------------------------------------------
# BROADCAST — icecast2 + liquidsoap in one container
# -------------------------------------------------------------------------
broadcast:
image: ghcr.io/perminder-klair/subwave-broadcast:latest
container_name: sub-wave-broadcast
restart: unless-stopped
logging: *default-logging
environment:
- TZ=America/New_York
ports:
- 7702:7702
volumes:
- /srv/subwave/state:/var/sub-wave
- /srv/subwave/logs:/var/log/liquidsoap
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:7702/status-json.xsl > /dev/null"]
interval: 5s
timeout: 3s
retries: 12
start_period: 15s
networks:
- npm-network
# -------------------------------------------------------------------------
# CONTROLLER — AI DJ brain + admin API
# -------------------------------------------------------------------------
controller:
image: ghcr.io/perminder-klair/subwave-controller:latest
container_name: sub-wave-controller
restart: unless-stopped
logging: *default-logging
depends_on:
broadcast:
condition: service_healthy
docker-socket-proxy:
condition: service_started
environment:
- NODE_ENV=production
- TZ=America/New_York
- STATE_DIR=/var/sub-wave
- SOUNDS_DIR=/sounds
- ANALYZE_URL=http://analyzer:8080
- DOCKER_HOST=tcp://docker-socket-proxy:2375
env_file:
- ./.env
ports:
- 7701:7701
volumes:
- /srv/subwave/state:/var/sub-wave
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:7701/health > /dev/null"]
interval: 10s
timeout: 5s
retries: 6
start_period: 20s
networks:
- npm-network
# -------------------------------------------------------------------------
# DOCKER-SOCKET-PROXY — locked-down, read-only Docker API for the admin
# Stats panel. Remove this + the controller's DOCKER_HOST/depends_on entry
# for zero Docker-socket exposure.
# -------------------------------------------------------------------------
docker-socket-proxy:
image: ghcr.io/tecnativa/docker-socket-proxy:0.3.0
container_name: sub-wave-docker-proxy
restart: unless-stopped
logging: *default-logging
environment:
- CONTAINERS=1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- npm-network
# -------------------------------------------------------------------------
# WEB — Next.js listener UI + admin console
# -------------------------------------------------------------------------
web:
image: ghcr.io/perminder-klair/subwave-web:latest
container_name: sub-wave-web
restart: unless-stopped
logging: *default-logging
depends_on:
controller:
condition: service_healthy
environment:
- NODE_ENV=production
- SUBWAVE_HOMEPAGE=player
- CONTROLLER_INTERNAL_URL=http://controller:7701
env_file:
- ./.env
ports:
- 7700:7700
networks:
- npm-network
# -------------------------------------------------------------------------
# ANALYZER — lean acoustic-analysis sidecar (bpm/key/loudness/outro
# detection for ending-aware crossfades). No GPU needed.
# -------------------------------------------------------------------------
analyzer:
image: ghcr.io/perminder-klair/subwave-analyzer:latest
container_name: sub-wave-analyzer
restart: unless-stopped
logging: *default-logging
mem_limit: 6g
volumes:
- /srv/subwave/state:/var/sub-wave
- analyzer-cache:/opt/analyzer/hf-cache
networks:
- npm-network
volumes:
analyzer-cache:
networks:
npm-network:
external: true