A couple of years ago, I installed Owncast1, and then did nothing at all with it, so this year I decided to keep the trend going and install icecast.
Icecast is a little bit different than Owncast. Owncast wants to be a twitch.tv clone, icecast is for making an internet radio station. I'm still finagling with it, so these notes might change over time, but lets' get this thing rolling.
I already had a domain name, so that was taken care of, and I already had the Owncast server mostly configured, so I used that with some tweaks.
Installing software
You need a web server, and I'll use nginx
for this apt install nginx
. It turns out that icecast development appears to be glacial enough that the current version is in Debian's repos. So just using apt install icecast2
should install just about everything else that's needed. It asks for some configuration, and you can set everything now or wait until later. It doesn't make much difference. I'd also recommend installing ufw
, lame
, madplay
, and ezstream
for later. It also sets up a user, icecast2
, so take a note of that.
Configuring nginx
I wanted to have a website for whatever information I wanted to give to any visitors, but I also want to have the stream be available, too. And since most browsers gripe if you don't have SSL on your site, I wanted to set that up, too. My first idea was to use a reverse proxy, but that wasn't really needed. I just have icecast listening on a different port
I went to /etc/nginx/sites-available
and created a file with the website's name. I made the following changes (mostly copied from my Owncast config):
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
# SSL configuration
#
listen 443 ssl http2 ;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
ssl_certificate /etc/ssl/certs/example-chain.crt;
ssl_certificate_key /etc/ssl/private/example.key;
# ssl_prefer_server_ciphers on;
# gzip on;
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;
root /var/www/example.com/webroot;
# Add index.php to the list if you are using PHP
index index.html index.htm;
server_name example.com www.example.com;
autoindex on;
location / {
}
error_page 404 /errors/404.html;
location /errors/404.html {
internal;
}
}
This is pretty basic. The only special thing it does is create an HTTPS site and redirect anyone coming in from plain HTTP to HTTPS.You'll want to change the site names and directories as appropriate. Once you've done that, you'll want to make a symbolic link in # ln -s /etc/nginx/sites-available/yoursite /etc/nginx/sites-enabled/yoursite
. Once you've done that, it's time to create our SSL certificate request.
Most people would tell you to use Let's Encrypt for an SSL certificate. I'm not most people. Instead, I created a certificate signing request: openssl req -new -newkey rsa:4096 -days 365 -nodes -keyout yourdomain.key -out yourdomain.csr
. Keep the .key file private and send the CSR file to your certificate vendor. Follow their validation instructions and they'll send you a bunch of files. Extract them to a convenient location (i.e. somewhere where your key file is) and combine them to get your certificate
cat yourdomain.crt > yourdomain-chain.crt ; echo >> yourdomain-chain.crt ; cat yourdomain_ca-bundle >> yourdomain-chain.crt
Now might also be a good time to generate an SSL certificate for your Icecast install. You can't just use the same file, though. Icecast expects to have the private key as well
cat yourdomain.crt yourdomain.ca-bundle yourdomain.key >> yourdomain-icecast.crt
Put yourdomain.ca-bundle in /etc/ssl/certs/example-chain.crt
and your key file in /etc/ssl/private/example.key
2. If all goes well, you can restart nginx # service nginx restart
and if it doesn't complain, we can move on to the next step. Put yourdomain-icecast.crt somewhere that icecast can get to it, and update the config file appropriately.
# ufw allow 80
# ufw allow 443
# ufw allow 8000
Setting up the icecast service
By default, icecast ships with a config file at /etc/icecast2/icecast.xml
. Make a backup of this file and edit it to set up your server. The file is pretty well documented, but there's a lot here, and a big ol' warning about not going too crazy configuring every setting. I only set the timeout, the passwords, the server URLs, and the 'information'. I will probably continue tweaking this file until something breaks. I also enabled SSL and filled in the path where I copied the special icecast certificate (I used the same 'certs' folder above, I just gave the icecast cert its own name).
Big ol' warning here: while SSL seemed to work, I could connect to the stream and I could listen to it, whenever I'd change sources the icecast server would hang and CPU usage would spike to 100% until it was killed. It's possible that I screwed something up, so I'm sticking with an unencrypted stream for now.
Setting up authentication
You probably want to set up some basic authentication so people can't get into your server without putting forth a little bit of effort. There are three passwords you want to set: source, relay, and admin. They all follow the same format and are in the <authentication>
section.
<authentication>
<!-- Sources log in with username 'source' -->
<source-password>changeme</source-password>
<!-- Relays log in with username 'relay' -->
<relay-password>changeMeToo</relay-password>
<!-- Admin logs in with the username given below -->
<admin-user>admin</admin-user>
<admin-password>alsoChangeMe</admin-password>
The default username to connect to the streaming server is 'source', and this sets the 'source' password. You can change this and the username for the other mountpoints. You probably want to do this.
The 'relay' password is for setting up relays between icecast servers. Relays are outside the scope of this document.
You can change the username that the admin user uses, as well as set a password. You probably want to change both of these things to something that's not easily guessable.
You can test your admin credentials by going to http://yourserver.com:8000/admin/. Note that you >must put in the trailing slash, otherwise you won't be able to connect (it's picky). If you're able to sign in and see all kinds of wonderful stats about your server, then you're in. Actually doing administration is beyond the scope of this page. You probably want to peruse the icecast docs.
Setting up mountpoints
Just a note here, this can get very hairy very quickly. My advice is to take it slow and read the documentation a few times.
In Icecast-land, a mountpoint
is a place on your server that you broadcast to and your listeners broadcast from. You can set up multiple mountpoints for different things, but I'm only going to cover a basic mountpoint setup for listeners to connect to and that you can override with live content if you want.
For this, I use two mountpoints, one for program to continuously run through a playlist of mp3 files and one that I connect to that overrides the stream when I want to go live.
First, we need a listen-socket
block
<listen-socket>
<port>8000</port>
<ssl>0</ssl>
<shoutcast-mount>/broadcast</shoutcast-mount>
</listen-socket>
Next we need to set up another block that tells icecast to play one mountpoint, but if it's not available, use the other one. It's a little bit tricky to wrap your head around, but the code block I used looks like this:
<!-- Set up a 'fallback' mount to use all the time, except when someone is live -->
<mount type="normal">
<mount-name>/broadcast</mount-name>
<max-listeners>100</max-listeners>
<burst-size>65536</burst-size>
<fallback-mount>/stream</fallback-mount>
<fallback-override>1</fallback-override>
<fallback-when-full>1</fallback-when-full>
<hidden>0</hidden>
<public>1</public>
</mount>
It's a little bit confusing, but it basically says that if the /broadcast
mountpoint is available, play that, but if it goes away, play /stream
instead. Now we need to set up something to broadcast to our server so that we have constant content in between our live shows.
Setting up ezstream
The icecast docs recommend a program called ezstream
that happened to be in my distro's repositories, so it was just a matter of typing apt install ezstream
, making a copy of /usr/share/doc/ezstream/examples/ezstream-full.xml
and making a few changes.
This file has every possible option defined, but you only really need to pay attention to a few of them. You want to change the <server>
option to configure your connection to the server you're trying to connect to. You want to configure hostname
and port
, and you want to pay attention to is TLS. I had a problem getting TLS working reliably on icecast
, but for this connection it doesn't matter much. I installed this on the same server that icecast is running on, so the connection doesn't actually go out over the internet anywhere, so I left TLS off for this, and I set the format to be MP3.
Fill out the informational settings with whatever you want. Ideally they will match or strongly resemble what you're actually streaming.
The ezstream documentation doesn't explain this very well, but you need to set up an 'intake' for it to play. An intake, as far as I can tell, is a set of options for broadcasting to a particular server. The relevant portion of my config is below:
/var/www/icecast/ezstream.xml
<?xml version="1.0" encoding="UTF-8"?>
<ezstream>
<!--
Server configuration
-->
<servers>
<server>
<!-- Identifying name (default: "default") -->
<name>Your Server Name</name>
<protocol>HTTP</protocol>
<hostname>yourhostname.radio</hostname>
<port>8888</port>
<!-- Login user (default: "source") -->
<user>source</user>
<!-- Login password (check file permissions, or everyone can see this) -->
<password>YourSuperSecurePassword</password>
<tls>May</tls>
<!-- Number of reconnection attempts, before giving up (default: 0) -->
<reconnect_attempts>20</reconnect_attempts>
</server>
</servers>
<!--
Stream configuration
-->
<streams>
<stream>
<!--
Identifying name (default: "default")
NB: Only the "default" stream is supported at this point.
-->
<!-- <name>default</name> -->
<!-- Mount point on server -->
<mountpoint>/stream</mountpoint>
<!-- Name of the intake entry to use (default: "default") -->
<intake>Your Radio Station</intake>
<!-- Name of the server entry to use (default: "default") -->
<server>yourhostname.radio</server>
<!--
Setting to allow stream to be listed in public YP directory
(default: no)
-->
<public>No</public>
<!-- Stream format: Ogg, MP3, WebM, Matroska -->
<format>MP3</format>
<!-- Encoder name (defined below) to use for (re)encoding -->
<encoder>Lame-CBR128</encoder>
<!-- Various other informational settings -->
<stream_name>Your Super Cool stream</stream_name>
<stream_url>http://yourhostname.radio:8888/stream.mp3</stream_url>
<stream_genre>The things you hear</stream_genre>
<stream_description>For your ears only</stream_description>
<stream_quality>1.5</stream_quality>
<stream_bitrate>16</stream_bitrate>
<stream_samplerate>44100</stream_samplerate>
<stream_channels>2</stream_channels>
</stream>
</streams>
<!--
Intake configuration
-->
<intakes>
<intake>
<!-- Identifying name (default: "default") -->
<name>Your Cool Radio</name>
<!--
Media type: autodetect, file, playlist, program, stdin
(default: autodetect)
-->
<type>playlist</type>
<!-- Input file, program name, or "stdin" keyword (deprecated) -->
<filename>/var/www/icecast/playlist.txt</filename>
<!-- Setting to shuffle playlists -->
<shuffle>No</shuffle>
<!-- Setting whether to stream intake indefinitely or only once -->
<stream_once>No</stream_once>
</intake>
</intakes>
<!--
Metadata configuration
-->
<metadata>
<!-- Program to query for metadata instead of from media files -->
<!-- <program>meta.sh</program> -->
<!-- Metadata format -->
<!-- <format_str>@a@ - @t@</format_str> -->
<format_str>@a@ - @T@</format_str>
<!-- Interval for additional metadata refreshes (default: -1 (none)) -->
<refresh_interval>-1</refresh_interval>
<!-- Setting to clean up extraneous whitespace (default: no) -->
<normalize_strings>Yes</normalize_strings>
<!-- Setting to suppress all metadata udpates (default: no) -->
<no_updates>No</no_updates>
</metadata>
<!--
Decoder configurations
-->
<decoders>
<decoder>
<!-- Decoder name -->
<name>OggDec</name>
<!-- Program and options -->
<program>oggdec -R -b 16 -e 1 -s 1 -o - @T@</program>
<!-- File extensions to use this decoder for -->
<file_ext>.ogg</file_ext>
<file_ext>.oga</file_ext>
</decoder>
<decoder>
<name>MadPlay</name>
<program>madplay -b 16 -R 44100 -S -o raw:- @T@</program>
<file_ext>.mp3</file_ext>
</decoder>
<decoder>
<name>Flac</name>
<program>flac -s -d --force-raw-format --sign=signed --endian=little -o - @T@</program>
<file_ext>.flac</file_ext>
</decoder>
<decoder>
<name>AAC</name>
<program>faad -d -f 2 -w @T@</program>
<file_ext>.m4a</file_ext>
</decoder>
</decoders>
<!--
Encoder configurations
-->
<encoders>
<encoder>
<!-- Encoder name -->
<name>OggEnc-Q1.5</name>
<!-- Output stream format -->
<format>Ogg</format>
<!-- Program and options -->
<program>oggenc -r -B 16 -C 2 -R 44100 --raw-endianness 0 -q 1.5 -t @M@ -</program>
</encoder>
<encoder>
<name>Opus-VBR96</name>
<format>Ogg</format>
<program>opusenc --vbr --bitrate 96 --title @t@ --artist @a@ --raw --raw-rate 44100 - -</program>
</encoder>
<encoder>
<name>Lame-CBR128</name>
<format>MP3</format>
<program>lame --preset cbr 128 -r -s 44.1 --bitwidth 16 - -</program>
</encoder>
</encoders>
</ezstream>
Most of the rest of these options should be (mostly) self-explanatory. You want to make sure that you have the appropriate software installed in the decoders
section to play the files you want to play, and then you'll want to make sure that you have the correct encoder to send it off to your icecast server.
I just stuck a bunch of .mp3 files in a folder, did a ls *.mp3 | shuf >> playlist.txt
to get a list of all the files, put them in a random order, and then write the list into playlist.txt
, and then I can start ezstream and point it to my config file by executing /usr/bin/ezstream -c /var/www/icecast/ezstream-full.xml
, but that requires me to leave a terminal window open all the time, so once I'm satisfied that it all works, I set up systemd units.
Setting up systemd units
Once you're satisfied with your configs and everything is working, it might be useful to set up a couple of systemd units to start them up as a service. That way you can reboot your server and not have to go and start everything manually. Go to /etc/systemd/system
and create two files: I used icecast2.service
and ezstream.service
. Fill them out appropriately
/etc/systemd/system/icecast2.service
[Unit]
Description=icecast
Before=ezstream
[Service]
Type=simple
User=icecast2
ExecStart=/usr/bin/icecast2 -c /etc/icecast2/icecast.xml
[Install]
WantedBy=multi-user.target
/etc/systemd/system/ezstream.service
[Unit]
Description=ezstream
After=icecast2.service
Requires=icecast2.service
[Service]
Type=simple
User=icecast2
WorkingDirectory=/var/www/icecast
ExecStart=/usr/bin/ezstream -c /var/www/icecast/ezstream-full.xml
[Install]
WantedBy=multi-user.target
There are a few important things here: the User=icecast2
means that the services will be running as the icecast2
user, and the ExecStart
lines start the programs with their config files that we spent so long setting up. Pay special attention to the Before
, After
, and Requires
. This tells systemd to start the services in order. You need to have icecast
running before ezstream
can do anything. service start icecast2
and visiting your website.com:8888 (or whatever port you specified). If you see the icecast interface, you're ready to test. You can then service start ezstream
or use your favorite broadcasting tool to go live (both of which are beyond the scope of this article which is already way too long). Once you declare it working you might want to consider having these services start at boot time.
At this point you should have a working server, but there's a lot to be done. You might consider locking down the admin interface by using a firewall rule or SSL or something. You will want to configure some client software to stream from. You might want to create a web page so visitors to your site will have something pretty to look at. The possibilities are endless.