VOL. I  ·  NO. 
SUB/WAVE
ON AIR

SETUP · 03

Run the commands yourself.

The same outcome as the wizard, just without the wizard wrapping it. Useful if you're scripting the install, want a non-standard layout, or just prefer running each command by hand. These six steps land at a public-facing single-host deploy — Caddy on the edge, Cloudflare in front, internal-only Icecast, Controller, and Web.

01

Clone the repo

git clone https://github.com/perminder-klair/subwave.git
cd subwave
02

Tell the controller where your Navidrome library lives

Copy the template and fill in your values:

cp controller/.env.example controller/.env
$EDITOR controller/.env

The values that actually matter — the rest of the template has good defaults. The LLM provider and model are chosen later, in the admin Settings UI, not here:

# controller/.env — point SUB/WAVE at your services

# Navidrome (or any Subsonic-API server)
NAVIDROME_URL=http://navidrome.local:4533
NAVIDROME_USER=your-username
NAVIDROME_PASS=your-password

# (Optional) If the controller can read your music files directly from
# disk, set this to the mount path — skips streaming over HTTP.
# MUSIC_LIBRARY_PATH=/music

# LLM — the active provider + model are chosen in the admin Settings UI,
# not here. Ollama (the homelab default) needs no key. Only set a cloud
# key below if you switch to a hosted provider in Settings.
# ANTHROPIC_API_KEY=
# OPENAI_API_KEY=
# OPENROUTER_API_KEY=
# DEEPSEEK_API_KEY=
# ELEVENLABS_API_KEY=     # only for the 'cloud' TTS voice

# Icecast source password (any string; just match the docker-compose env)
ICECAST_SOURCE_PASSWORD=replace-me-with-a-strong-string

# Admin auth — gates the /admin console. REQUIRED in production (the
# controller refuses to boot without it); optional for local dev.
ADMIN_USER=admin
ADMIN_PASS=replace-me
CONNECTION TEST

Before booting the stack, sanity-check Navidrome from your terminal:

curl "$NAVIDROME_URL/rest/ping.view?u=$NAVIDROME_USER&p=$NAVIDROME_PASS&v=1.16.1&c=sub-wave&f=json"

You should get back { "subsonic-response": { "status": "ok" ... } }.

03

Configure the broadcast layer

Icecast needs three passwords and a state directory the containers can share. scripts/setup.sh renders the Icecast config from a template; running it once is enough.

# docker/.env
ICECAST_SOURCE_PASSWORD=replace-me-with-a-strong-string
ICECAST_ADMIN_PASSWORD=another-strong-string
ICECAST_RELAY_PASSWORD=another-strong-string
SUBWAVE_HOMEPAGE=landing
# STATE_DIR=/srv/subwave   # optional — defaults to <repo>/state
sudo ./scripts/setup.sh   # state defaults to <repo>/state

STATE_DIR is where Liquidsoap, the controller, and the web container exchange files — next track, voice WAVs, now-playing. Anything that survives docker compose down lives there.

04

Boot the stack

docker compose -f docker/docker-compose.prod.yml up -d --build

What just started:

  • icecast — broadcast endpoint, internal-only
  • liquidsoap — mixer feeding Icecast
  • controller — the DJ brain; the one talking to Navidrome and Ollama
  • web — Next.js UI, internal-only
  • caddy — the only thing bound to a host port (:4800)

Generate the Piper station idents the first time:

./scripts/generate-jingles.sh
05

Tune in

open http://localhost:4800

Behind a domain? Put it behind Cloudflare or Tailscale; Caddy has auto_https off, so terminate TLS upstream.

EDIT THE DJ

Sign in to the admin console at /admin with the ADMIN_USER / ADMIN_PASS you set earlier. Build a roster of DJ personas — each with its own name, soul, voice, and skills — pick the LLM provider, and paint the weekly Shows schedule. Persona changes apply on the next intro, no restart needed.

06

Verify the broadcast

The repo ships a health probe that checks the containers, hits /api/health and /api/now-playing, and scans recent logs for errors. Run it after any deploy:

./scripts/health-check.sh

Auto-detects which compose file is live and which host port Caddy is mapped to. Exits 0 if healthy. Safe to wire into cron or a status page.

WHAT'S NEXT

Keep it running.

The stack is on the air. When a new version lands, head to Updates & Help for the rebuild-only-what-changed workflow and the troubleshooting checklist.