This write-up documents the radio stack we just set up for Mediabot v3 on Debian 13.
The goal was not to touch the existing production stream on port 15000, but to build a clean test instance on port 8000:
That separation matters. It lets you test and iterate without risking the production radio.
icecast2 is available in Debian 13, and Icecast itself is controlled primarily through an XML configuration file passed with -c. Debian’s own manpage explicitly says the XML config is the source of truth for runtime behavior.
Install the package:
apt update
apt install icecast2
For convenience during testing, also install a couple of tools:
apt install curl jq
Do not reuse the production config file directly. Create a dedicated one for the test instance.
Create /etc/icecast2/icecast-8000.xml:
<icecast>
<location>Earth</location>
<admin>icemaster@localhost</admin>
<limits>
<clients>50</clients>
<sources>3</sources>
<queue-size>524288</queue-size>
<client-timeout>30</client-timeout>
<header-timeout>15</header-timeout>
<source-timeout>10</source-timeout>
<burst-on-connect>1</burst-on-connect>
<burst-size>65535</burst-size>
</limits>
<authentication>
<source-password>w3bradiale</source-password>
<relay-password>ic3cast4dmin#</relay-password>
<admin-user>admin</admin-user>
<admin-password>**********</admin-password>
</authentication>
<hostname>teuk.org</hostname>
<listen-socket>
<port>8000</port>
</listen-socket>
<http-headers>
<header name="Access-Control-Allow-Origin" value="*" />
</http-headers>
<fileserve>1</fileserve>
<paths>
<basedir>/usr/share/icecast2</basedir>
<logdir>/var/log/icecast2-8000</logdir>
<webroot>/usr/share/icecast2/web</webroot>
<adminroot>/usr/share/icecast2/admin</adminroot>
<pidfile>/run/icecast2-8000/icecast.pid</pidfile>
<alias source="/" destination="/status.xsl"/>
</paths>
<logging>
<accesslog>access.log</accesslog>
<errorlog>error.log</errorlog>
<loglevel>3</loglevel>
<logsize>10000</logsize>
</logging>
<security>
<chroot>0</chroot>
</security>
<mount type="default">
<charset>UTF-8</charset>
</mount>
</icecast>
Create the runtime and log directories:
mkdir -p /var/log/icecast2-8000
mkdir -p /run/icecast2-8000
chown -R icecast2:icecast2 /var/log/icecast2-8000 /run/icecast2-8000
chmod 755 /var/log/icecast2-8000 /run/icecast2-8000
Validate the config before starting the service:
icecast2 -t -c /etc/icecast2/icecast-8000.xml
Create /etc/systemd/system/icecast2-8000.service:
[Unit]
Description=Icecast2 test instance on port 8000
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=icecast2
Group=icecast2
RuntimeDirectory=icecast2-8000
RuntimeDirectoryMode=0755
ExecStart=/usr/bin/icecast2 -c /etc/icecast2/icecast-8000.xml
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Then enable and start it:
systemctl daemon-reload
systemctl enable --now icecast2-8000.service
Check that it is listening:
ss -lntp | egrep ':8000\b'
systemctl status icecast2-8000.service --no-pager -l
liquidsoap is also available in Debian 13. Debian describes it as a scripting language for complex audio streaming systems, especially Internet radio/icecast setups. citeturn938244search1turn938244search3
Install it with:
apt update
apt install liquidsoap
The working model here is deliberately simple:
.m3u)8000Create /etc/liquidsoap/radio-8000.liq:
#!/usr/bin/liquidsoap --debug -U
settings.init.allow_root.set(false)
set("log.file.path","/var/log/liquidsoap/liquidsoap-8000.log")
set("log.level",3)
set("scheduler.log",true)
set("decoder.debug",false)
# systemd manages the process, not Liquidsoap itself
set("init.daemon",false)
set("server.telnet",true)
set("server.telnet.bind_addr","127.0.0.1")
set("server.telnet.port",1235)
set("harbor.reverse_dns",true)
set("audio.converter.samplerate.libsamplerate.quality","fast")
global_playlist = playlist("/home/airtime/m3u/playlist_23052014.m3u",reload=0,reload_mode="rounds")
global_playlist = audio_to_stereo(global_playlist)
live = input.harbor("/",port=18005,password="w3bradiale",icy=true)
radio = global_playlist
full = fallback(track_sensitive=false,[live,radio])
output.icecast(%mp3(stereo=true, samplerate=44100, bitrate=160),
host="localhost",
port=8000,
password="**********",
mount="/radio160.mp3",
description="TeuKRadio TEST",
url="http://teuk.org",
public=false,
fallible=true,
full)
output.icecast(%mp3(stereo=true, samplerate=44100, bitrate=320),
host="localhost",
port=8000,
password="**********",
mount="/radio320.mp3",
description="TeuKRadio TEST",
url="http://teuk.org",
public=false,
fallible=true,
full)
output.icecast(%mp3(stereo=true, samplerate=44100, bitrate=64),
host="localhost",
port=8000,
password="**********",
mount="/radio64.mp3",
description="TeuKRadio TEST",
url="http://teuk.org",
public=false,
fallible=true,
full)
set("init.daemon",false) is important because systemd is supervising the process. Letting Liquidsoap daemonize itself under systemd is asking for trouble.playlist("/home/airtime/m3u/playlist_23052014.m3u",reload=0,reload_mode="rounds")
That means Liquidsoap follows the file exactly in the order defined there, round after round.
Create the log directory if needed:
mkdir -p /var/log/liquidsoap
chown -R liquidsoap:liquidsoap /var/log/liquidsoap
chmod 755 /var/log/liquidsoap
Validate the script before starting the service:
liquidsoap -c /etc/liquidsoap/radio-8000.liq
Create /etc/systemd/system/liquidsoap-radio8000.service:
[Unit]
Description=Liquidsoap test instance for Icecast2 port 8000
After=network-online.target icecast2-8000.service
Wants=network-online.target
Requires=icecast2-8000.service
[Service]
Type=simple
User=liquidsoap
Group=liquidsoap
RuntimeDirectory=liquidsoap
RuntimeDirectoryMode=0755
ExecStart=/usr/bin/liquidsoap /etc/liquidsoap/radio-8000.liq
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Enable and start it:
systemctl daemon-reload
systemctl enable --now liquidsoap-radio8000.service
Check the service and listening sockets:
systemctl status liquidsoap-radio8000.service --no-pager -l
ss -lntp | egrep ':1235\b|:18005\b|:8000\b'
tail -n 100 /var/log/liquidsoap/liquidsoap-8000.log
Do not guess. Query the local instance directly.
Check JSON status:
curl -s http://127.0.0.1:8000/status-json.xsl | jq .
Check admin stats:
curl -s -u admin:'**********' http://127.0.0.1:8000/admin/stats
A working instance should expose mounts such as:
/radio64.mp3/radio160.mp3/radio320.mp3This is exactly the kind of data Mediabot now reads from Icecast JSON. The project roadmap explicitly aimed for a radio service on 8000 that Mediabot can observe cleanly before moving on to the web UI. citeturn25file4turn25file5
The current sample config still shows the older [radio] keys like RADIO_PORT, RADIO_JSON, RADIO_HOSTNAME, RADIO_URL, and Liquidsoap telnet settings. fileciteturn25file0
For the new Icecast-based status flow, add these keys to the [radio] section of mediabot.conf:
[radio]
RADIO_PUB=900
RADIO_PORT=8000
RADIO_SOURCE=0
YOUTUBEDL_INCOMING=/home/mediabot/mediabot_v3/mp3
LIQUIDSOAP_PLAYLIST=plglob(dot)m3u
RADIO_ADMINPASS=**********
LIQUIDSOAP_TELNET_PORT=1234
RADIO_HOSTNAME=myradio.com
YTDLP_PATH=/usr/local/bin/yt-dlp
LIQUIDSOAP_TELNET_HOST=localhost
RADIO_URL=radio.mp3
RADIO_JSON=status-json.xsl
RADIO_ICECAST_STATUS_BASE_URL=http://127.0.0.1:8000
RADIO_ICECAST_PUBLIC_BASE_URL=http://teuk.org:8000
RADIO_ICECAST_TIMEOUT=5
RADIO_ICECAST_PRIMARY_MOUNT=/radio160.mp3
These new keys are used by the new Icecast reader and by the local radio JSON API exposed by Mediabot.
The live mediabot.conf you shared still only had the older radio keys before this update, so adding the RADIO_ICECAST_* values is required for the new path to work cleanly. fileciteturn25file3
If this setup is going to live in the repository, commit sample files too. At minimum:
contrib/icecast2/icecast-8000.sample.xmlcontrib/liquidsoap/radio-8000.sample.liqmediabot.sample.confThat way the repository documents not just the bot side, but the service layout around it.
The existing mediabot.sample.conf still reflects the older radio section and should be updated with the new RADIO_ICECAST_* keys. fileciteturn25file0
A quick checklist:
# Icecast
systemctl status icecast2-8000.service --no-pager -l
curl -s http://127.0.0.1:8000/status-json.xsl | jq .
# Liquidsoap
systemctl status liquidsoap-radio8000.service --no-pager -l
tail -n 100 /var/log/liquidsoap/liquidsoap-8000.log
# Mediabot local radio API
curl -s http://127.0.0.1:9108/api/radio/status | jq .
If all of that works, you have:
800015000The roadmap was very clear: first stabilize the bot, then build a clean radio brick on 8000, then expose that state to the future web UI. fileciteturn25file1turn25file4turn25file5
That is exactly what this setup achieves.
Instead of coupling everything too early, the stack is now split into sane layers:
That is a much better base than trying to bolt new features directly onto the old production radio flow.
You must be logged in to reply.