Configuration
MPD
MPD's config lives at /etc/mpd.conf. Key settings:
music_directory "/mnt/music"
playlist_directory "/var/lib/mpd/playlists"
db_file "/var/lib/mpd/tag_cache"
user "mpd"
bind_to_address "0.0.0.0"
port "6600"
Audio output — Icecast stream:
audio_output {
type "shout"
name "Radio Stream"
host "localhost"
port "8000"
mount "/radio.mp3"
password "YOUR_ICECAST_SOURCE_PASSWORD"
format "44100:16:2"
encoding "mp3"
bitrate "128"
}
The password here must match the <source-password> in your Icecast config. If they don't match, MPD will fail to connect to Icecast and you'll get silence.
Buffer tuning for the Pi:
audio_buffer_size "8192"
buffer_before_play "10%"
max_output_buffer_size "32768"
These values are conservative — tuned to avoid buffer underruns on the Pi's limited RAM.
After editing, restart MPD:
sudo systemctl restart mpd
Icecast2
Edit /etc/icecast2/icecast.xml. The important section:
<authentication>
<source-password>YOUR_SOURCE_PASSWORD</source-password>
<relay-password>YOUR_RELAY_PASSWORD</relay-password>
<admin-user>admin</admin-user>
<admin-password>YOUR_ADMIN_PASSWORD</admin-password>
</authentication>
<hostname>your-pi-ip</hostname>
<listen-socket>
<port>8000</port>
</listen-socket>
The <source-password> must match the password in mpd.conf. Set the others to something strong.
Allow cross-origin requests (needed for the web player):
<http-headers>
<header name="Access-Control-Allow-Origin" value="*" />
</http-headers>
Restart Icecast after changes:
sudo systemctl restart icecast2
Nginx
The Nginx config proxies everything through a single host. Create /etc/nginx/sites-enabled/radio:
server {
listen 80;
root /var/www/radio/public;
index index.html;
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache, must-revalidate";
}
location /api/ {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /stream {
proxy_pass http://127.0.0.1:5000/stream$is_args$args;
proxy_buffering off;
proxy_read_timeout 300s;
}
location /live {
proxy_pass http://127.0.0.1:8000/radio.mp3;
}
}
Remove the default site and reload:
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
The Programming Schedule
The schedule is stored in schedule.json — a simple JSON file that maps hours of the day to programming types:
{
"schedule": [
{ "hour": 6, "type": "genre", "value": "Rock" },
{ "hour": 10, "type": "podcast", "value": "podcasts" },
{ "hour": 11, "type": "genre", "value": "Reggae" },
{ "hour": 17, "type": "artist", "value": "The Tragically Hip" },
{ "hour": 21, "type": "genre", "value": "Folk" },
{ "hour": 22, "type": "metal", "value": "metal" }
],
"default": "random"
}
Any hour not listed falls back to "random" — a shuffle of the full library. The scheduler reads this file every hour and switches the MPD queue accordingly.
You can edit the schedule through the web UI or directly on the Pi. Changes take effect at the next hourly cron run.
Secrets in the Frontend
The frontend JavaScript contains two placeholder strings that are replaced at deploy time:
INJECT_OPENWEATHER_API_KEY→ your actual API keyINJECT_MOCK_MODE→false(production) ortrue(staging)
This keeps secrets out of source control. The replacement is done by sed as part of the deploy script.