# VPS deployment Target: any VPS with Docker + Docker Compose, ports 80/443 free (and 8000 if you want plain HTTP for very old stereos). ## 1. Lay out the bundle ```bash ssh root@YOUR_VPS mkdir -p /opt/airstudio/web/ai-radio && cd /opt/airstudio # copy from the repo: docker-compose.yml, Dockerfile.streamer, icecast.xml, Caddyfile ``` ## 2. Configure ```bash cp .env.vps.example .env $EDITOR .env # set ICECAST_SOURCE_PASSWORD, station name, MQTT $EDITOR icecast.xml # set the 3 passwords + hostname $EDITOR Caddyfile # set your domain ``` `ICECAST_SOURCE_PASSWORD` in `.env` **must** match `` in `icecast.xml` — the streamer authenticates with it. ## 3. Firewall ```bash ufw allow 22,80,443/tcp # optional plain-HTTP fallback for old stereos: ufw allow 8000/tcp ``` ## 4. Boot ```bash docker compose up -d --build docker compose ps # icecast, radio-streamer, caddy all Up docker compose logs -f radio-streamer ``` Expected streamer log: `live on icecast:8000/ai-radio.mp3` then `played .mp3` lines as songs roll. ## 5. Verify ```bash # direct icecast: curl -sI http://localhost:8000/ai-radio.mp3 | head -1 # via caddy (after DNS + cert issuance): curl -sI https://radio.example.com/ai-radio.mp3 | head -1 ``` In a desktop player: open the URL — you should hear the shipped playlist looping. (Empty web root → streamer waits; run the shipper once first.) ## 6. Ship from the generator On the generator machine: ```bash SHIP_REMOTE_TARGET=root@YOUR_VPS:/opt/airstudio/web/ai-radio/ \ SHIP_SSH_KEY=~/.ssh/id_ed25519_vps \ LIBRARY_DIR=~/ai-radio/library \ go run ./cmd/shipper ``` New songs appear on air within one playlist lap. ## Operations - **Restart everything:** `docker compose restart` - **Update streamer code:** `docker compose build streamer && docker compose up -d streamer` - **Admin UI:** `http://:8000/admin/` (user `admin`, your admin password) — shows listeners, mount status, kick sources. - **Logs:** `docker compose logs -f icecast radio-streamer caddy` - **MQTT check:** `mosquitto_sub -h BROKER -t airstudio/nowplaying -v` ## Troubleshooting | Symptom | Cause / fix | |---|---| | streamer: `icecast rejected source 401` | password mismatch between `.env` and `icecast.xml` | | streamer: `no playable files` | shipper hasn't run; check `web/ai-radio/` contents | | silence but stream "connected" | stale playlist pointing at missing mp3s — re-run shipper | | caddy 502 | icecast container down: `docker compose ps` | | no HTTPS cert | DNS not pointing at this VPS yet; check `docker compose logs caddy` | | stereo shows garbage titles | very old ID3v1-only player: titles come from ID3v1 — re-transcode (already written by shipper) |