# 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