VOL. I  ·  NO. 
SUB/WAVE
ON AIR

SETUP · 04

Hacking on SUB/WAVE.

Three compose files, three deployment shapes. Icecast and Liquidsoap live together in a single broadcast container in every shape. Dev mode runs the radio backend in Docker and the Next.js UI on the host with hot reload, so you can iterate 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           (hot-reloaded UI)
npm run rebuild      # docker compose up -d --build  (rarely needed in dev — 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

# editing web/** — saves are hot-reloaded, no docker action needed.
# editing controller/src/** — tsx watch restarts the process in-place.
# editing liquidsoap/radio.liq:
docker compose -f docker-compose.dev.yml restart broadcast   # bind-mounted in dev, no rebuild needed
# editing controller/src/** in prod, or any radio.liq change in prod:
docker compose up -d --build controller   # or 'broadcast' for radio.liq changes
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.