VOL. I  ·  NO. 
SUB/WAVE
ON AIR
No skips · No shuffle · Just radio

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 fileWhat runsState dir
docker-compose.ymlprod: broadcast · controller · web · caddy edge on :7700${STATE_DIR:-<repo>/state}
docker-compose.byo.ymlsame 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.ymldev: 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 setup

Interactive wizard: writes envs, brings the stack up.

npm run dev

Alias for setup; same wizard.

npm run dev:docker

docker compose up -d, radio backend only.

npm run dev:web

next dev on :7700, the hot-reloaded UI.

npm run rebuild

docker compose up -d --build. Rarely needed in dev, since controller/src and radio.liq are bind-mounted.

npm run logs

Tail docker logs.

npm run jingles

Render station idents via Piper.

npm run down

Stop the stack.

A TYPICAL SESSION

Backend in Docker, UI on the host.

One-time, in two terminals:

npm run dev:docker

Terminal 1, radio backend (broadcast + controller).

npm run dev:web

Terminal 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 broadcast

In 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 controller
DEV HOT-RELOADS · PROD NEEDS A REBUILD

In 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.