SETUP · 04
Hacking on SUB/WAVE.
Three compose files, three deployment shapes. In every one, Icecast and Liquidsoap share a single broadcast container. Dev mode runs the radio backend in Docker and the Next.js UI on the host with hot reload, so you can work on the web without a rebuild. The two prod variants differ only at the edge: one bundles Caddy, the other binds host ports for your own reverse proxy.
THE THREE STACKS
Compose files.
| Compose file | What runs | State dir |
|---|---|---|
docker-compose.yml | prod: broadcast · controller · web · caddy edge on :7700 | ${STATE_DIR:-<repo>/state} |
docker-compose.byo.yml | same as prod minus caddy: web, controller, broadcast on host ports :7700 / :7701 / :7702 for your own reverse proxy | ${STATE_DIR:-<repo>/state} |
docker-compose.dev.yml | dev: broadcast · controller with tsx watch hot-reload (web runs separately on host) | ./state |
DAY-TO-DAY COMMANDS
Everything's an npm script.
The compose flags are all wired into package.json so you don't have to remember them:
npm run setupInteractive wizard: writes envs, brings the stack up.
npm run devAlias for setup; same wizard.
npm run dev:dockerdocker compose up -d, radio backend only.
npm run dev:webnext dev on :7700, the hot-reloaded UI.
npm run rebuilddocker compose up -d --build. Rarely needed in dev, since controller/src and radio.liq are bind-mounted.
npm run logsTail docker logs.
npm run jinglesRender station idents via Piper.
npm run downStop the stack.
A TYPICAL SESSION
Backend in Docker, UI on the host.
One-time, in two terminals:
npm run dev:dockerTerminal 1, radio backend (broadcast + controller).
npm run dev:webTerminal 2, Next.js on http://localhost:7700.
From there, edits to web/** hot-reload with no Docker action, and controller/src/** restarts in place under tsx watch. A liquidsoap/radio.liq change in dev needs a broadcast restart:
docker compose -f docker-compose.dev.yml restart broadcastIn prod, a controller/src/** or radio.liq change needs a rebuild and recreate (swap controller for broadcast for radio.liq changes):
docker compose up -d --build controllerIn dev, the controller container bind-mounts controller/src/ and runs under tsx watch, so edits to controller/src/** restart the process inside the container automatically. liquidsoap/radio.liq is bind-mounted too; edits there need docker compose -f docker-compose.dev.yml restart broadcast but no rebuild.
In prod, both Dockerfiles COPY source at build time, so docker compose restart controller would rerun the same baked-in code. docker compose up -d --build <service> (against docker-compose.yml) is what you want when deploying a change.
The web dev server (npm run dev:web) is its own thing: Next.js hot-reloads, so edits to web/** show up instantly without touching Docker.