# systemd unit for the daveo-radio node app — /etc/systemd/system/radio.service # ============================================================================ # # An alternative to pm2 (ecosystem.config.cjs). Use ONE of the two, not both. # # Runs the app as a long-lived service behind Apache (see # apache-radio.conf.example). Adjust User/Group and WorkingDirectory to match # where the repo is checked out on the server. # # BUILD LOCALLY — this app serves the built bundle in app/, which is gitignored, # and systemd does NOT build. Code is synced by FTP, so build on your machine # (`npm run build`) and include app/ in the upload. The server only needs the # runtime deps: # # npm ci --omit=dev # 5 runtime deps only, no build tooling # # Then: # sudo cp deploy/radio.service.example /etc/systemd/system/radio.service # sudo systemctl daemon-reload # sudo systemctl enable --now radio # systemctl status radio # journalctl -u radio -f # logs (startup + connection events) # # Redeploy: `npm run build` locally -> FTP up app/ + changed files -> # sudo systemctl restart radio (npm ci again only if package.json changed) [Unit] Description=daveo-radio (Icecast player + socket.io chat, node) After=network.target [Service] Type=simple User=www-data Group=www-data WorkingDirectory=/var/www/radio ExecStart=/usr/bin/node app.js Restart=on-failure RestartSec=2 # Config. Mirrors .env.example. DAW_ORIGIN is intentionally absent — it is a # local-dev-only shim for the shared chrome and must NOT be set in production. Environment=NODE_ENV=production Environment=PORT=3000 Environment=BASE_PATH=/radio Environment=TRUST_PROXY=1 Environment=STREAM_URL=https://stream.davidawindham.com/stream Environment=STREAM_STATUS_URL=https://stream.davidawindham.com/status-json.xsl Environment=STREAM_MOUNT=/stream Environment=LASTFM_USER=windhamdavid # The Last.fm key is the one secret. Keep it out of this world-readable unit: # put it in a drop-in that root owns, chmod 600 -- # sudo systemctl edit radio # and add: # [Service] # Environment=LASTFM_API_KEY=xxxxxxxx # Unset => the sidebar lists stay empty; the app still runs. [Install] WantedBy=multi-user.target