Media & Entertainment AWS

Icecast 2.4.4 with Liquidsoap 2.4.6 on AWS User Guide

| Product: Icecast 2.4.4 with Liquidsoap 2.4.6 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Icecast 2.4.4 with Liquidsoap 2.4.6 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg media servers listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

The image is a complete internet radio station. Icecast, from the Xiph.Org Foundation, is the streaming server: it relays one incoming stream to many listeners and publishes a status page, playlists and an admin interface. Liquidsoap, from the Savonet project, is the programmable audio engine that feeds it: it plays playlists, mixes live shows, crossfades, normalises loudness and encodes the result into the stream. On this image Liquidsoap is already connected to Icecast and streaming, so a listener can tune in seconds after the instance boots.

What cloudimg adds:

  • A station that works on first boot. Liquidsoap runs as the cloudimg-radio service and streams a quiet 440 Hz reference tone, encoded as Ogg Vorbis, to https://<your-host>/stream.ogg. No third party audio ships on the image; Step 7 shows how to play your own music instead.
  • Liquidsoap 2.4.6 built from source, licence clean. The prebuilt Liquidsoap packages link the Fraunhofer FDK AAC codec, which may not be redistributed with GPL software. cloudimg compiles Liquidsoap 2.4.6 from its checksum verified release with Ogg, Vorbis, Opus, FLAC and LAME (MP3 encoding) support, and no FDK AAC or FFmpeg. The compiler and build tools are removed before the image is captured.
  • HTTPS from the first boot. nginx serves the status page, the admin interface and the listener streams on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • Source clients over TLS. Live presenters connect to port 8443 with TLS switched on, so the source password never crosses the network in clear text.
  • Three passwords unique to your instance. Admin, source and relay passwords are generated on first boot and written to /root/icecast-liquidsoap-credentials.txt. Liquidsoap receives only the source password, from a file readable by root and its own service. The upstream default password appears nowhere and never works.
  • Fail closed. Icecast and Liquidsoap refuse to start while their configuration still holds a placeholder or the upstream default, and nginx refuses to start until first boot has finished.
  • Icecast on loopback. Icecast listens only on 127.0.0.1:8000 and Liquidsoap listens on nothing; nginx is the only service on the network.
  • Hardened defaults. Icecast and Liquidsoap each run as their own unprivileged user in a systemd sandbox, Icecast, Liquidsoap and nginx are all blocked from the instance metadata service, and the AMI requires IMDSv2.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Icecast 2.4.4 (the stock Ubuntu 24.04 icecast2 package, so it inherits Ubuntu's security maintenance), Liquidsoap 2.4.6 in /opt/liquidsoap (compiled by cloudimg, with its complete corresponding source in /usr/local/src/liquidsoap-2.4.6-source), nginx with its stream module, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet.
  • A security group that allows:
  • TCP 22 (SSH) from your own networks only
  • TCP 443 (status page, admin interface and listeners) from wherever your listeners are, usually anywhere
  • TCP 80 (redirects to HTTPS) from the same sources as 443
  • TCP 8443 (live source clients over TLS) from your presenters' networks only

Encoding one Ogg Vorbis stream takes a small fraction of a CPU core and Icecast relays without re encoding, so listener count is mostly a bandwidth question. A 128 kbit/s stream to 100 listeners needs about 13 Mbit/s outbound.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Icecast 2.4.4 with Liquidsoap 2.4.6 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above.

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=radio}]'

Step 2: Connect to your instance

Version SSH user
Icecast 2.4.4 with Liquidsoap 2.4.6 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot generates the certificate and the three passwords, then starts Icecast, Liquidsoap and nginx:

systemctl is-active icecast2 cloudimg-radio nginx icecast-liquidsoap-firstboot
sudo test -f /var/lib/cloudimg/icecast-liquidsoap-firstboot.done && echo FIRSTBOOT_DONE
liquidsoap --version | head -n 1

All four units are active, the first boot marker is present and Liquidsoap reports its version:

active
active
active
active
FIRSTBOOT_DONE
Liquidsoap 2.4.6

nginx is the only service on public addresses (80, 443 and 8443); Icecast listens on loopback and Liquidsoap listens on nothing:

sudo ss -lntpH | awk '{split($6, a, "\""); print $4, a[2]}' | sort
0.0.0.0:22 sshd
0.0.0.0:443 nginx
0.0.0.0:80 nginx
0.0.0.0:8443 nginx
127.0.0.1:8000 icecast2
127.0.0.53%lo:53 systemd-resolve
127.0.0.54:53 systemd-resolve
[::]:22 sshd
[::]:443 nginx
[::]:80 nginx
[::]:8443 nginx

Step 4: Get your URLs and passwords

sudo cat /root/icecast-liquidsoap-credentials.txt

The file is readable by root only:

# Icecast 2.4.4 with Liquidsoap 2.4.6 - generated on first boot by icecast-liquidsoap-firstboot.service
# These passwords are unique to this instance; none was baked into the image. Keep this file secret.
# The three are independent: a presenter with the source password has no admin access.

ICECAST_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
ICECAST_STREAM_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/stream.ogg
ICECAST_ADMIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/admin/
ICECAST_ADMIN_USER=admin
ICECAST_ADMIN_PASSWORD=<ICECAST_ADMIN_PASSWORD>
ICECAST_SOURCE_HOST=ec2-<public-ip-dashed>.<region>.compute.amazonaws.com
ICECAST_SOURCE_PORT=8443
ICECAST_SOURCE_USER=source
ICECAST_SOURCE_PASSWORD=<ICECAST_SOURCE_PASSWORD>
ICECAST_RELAY_USER=relay
ICECAST_RELAY_PASSWORD=<ICECAST_RELAY_PASSWORD>

# Live stream:       ICECAST_STREAM_URL   (Liquidsoap plays a generated 440 Hz demo tone out of the box)
# Status page:       ICECAST_URL          (self-signed certificate minted on first boot)
# Admin UI:          ICECAST_ADMIN_URL    (user admin)
# Your own playlist: edit /opt/cloudimg-radio/cloudimg-radio.liq, then  sudo systemctl restart cloudimg-radio
# Your own source client (butt, Mixxx, OBS): server ICECAST_SOURCE_HOST, port 8443 with TLS/SSL on,
#   user source, password ICECAST_SOURCE_PASSWORD, a mount of your own such as /live.ogg
# Change the name it is served as:  sudo icecast-liquidsoap-set-hostname <your-dns-name>
# User guide: https://www.cloudimg.co.uk/guides/icecast-2-4-4-with-liquidsoap-2-4-6-on-ubuntu-24-04-aws/
  • ICECAST_STREAM_URL is the station's live stream. Open it in VLC, a browser or any player that understands Ogg Vorbis.
  • ICECAST_ADMIN_PASSWORD signs you in to the admin interface as user admin.
  • ICECAST_SOURCE_PASSWORD is what a source client uses, with user source, to broadcast. Liquidsoap on the instance uses it too.
  • ICECAST_RELAY_PASSWORD is used by another Icecast server pulling a relay from this one.

The URLs use the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To use your own DNS name, see Step 10. When you need to share the file, for example in a support request, print it with the passwords masked:

sudo sed -E 's/^(ICECAST_[A-Z_]*PASSWORD=).*/\1********/' /root/icecast-liquidsoap-credentials.txt | grep -E '^ICECAST_'

Step 5: Check the server from the command line

Port 80 redirects to HTTPS, the health check is open for load balancers, and Icecast reports its version:

H=$(sudo grep '^ICECAST_URL=' /root/icecast-liquidsoap-credentials.txt | sed -E 's#^ICECAST_URL=https://([^/]+)/.*#\1#')
curl -s -o /dev/null -w 'http://   -> %{http_code}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'healthz   -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/healthz"
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/status-json.xsl" | jq -r '.icestats.server_id'
http://   -> 301
healthz   -> HTTP 200
Icecast 2.4.4

The admin interface refuses a request with no password and the upstream default, and accepts this instance's admin password. The password is passed to curl on standard input, so it never appears in the process list or your shell history:

sudo bash -c '
  F=/root/icecast-liquidsoap-credentials.txt
  P=$(grep "^ICECAST_ADMIN_PASSWORD=" $F | cut -d= -f2-)
  H=$(grep "^ICECAST_URL=" $F | sed -E "s#^ICECAST_URL=https://([^/]+)/.*#\1#")
  R="--resolve $H:443:127.0.0.1"
  D="hack""me"
  curl -sk $R -o /dev/null -w "admin, no password       -> HTTP %{http_code}\n" "https://$H/admin/stats"
  printf "user = \"admin:%s\"\n" "$D" | curl -K - -sk $R -o /dev/null -w "admin, upstream default  -> HTTP %{http_code}\n" "https://$H/admin/stats"
  printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk $R -o /dev/null -w "admin, this instance     -> HTTP %{http_code}\n" "https://$H/admin/stats"
'
admin, no password       -> HTTP 401
admin, upstream default  -> HTTP 401
admin, this instance     -> HTTP 200

Step 6: Listen to the station

Liquidsoap is already on air. This reads the first few kilobytes of the live stream through nginx and shows what Icecast reports for the mountpoint:

H=$(sudo grep '^ICECAST_URL=' /root/icecast-liquidsoap-credentials.txt | sed -E 's#^ICECAST_URL=https://([^/]+)/.*#\1#')
curl -sk -m 5 --resolve "$H:443:127.0.0.1" "https://$H/stream.ogg" | head -c 4; echo
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/status-json.xsl" | jq -r '.icestats.source | .listenurl, .server_name, .server_type'
OggS
https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/stream.ogg
cloudimg radio
application/ogg

OggS is the start of an Ogg stream. On your own computer, open the ICECAST_STREAM_URL from Step 4 in VLC (Media > Open Network Stream) or in a browser, accepting the self signed certificate the first time, and you hear the reference tone. Players also accept the M3U playlist at https://<your-host>/stream.ogg.m3u.

Step 7: Play your own music with Liquidsoap

Liquidsoap reads its script from /opt/cloudimg-radio/cloudimg-radio.liq. The shipped script plays the reference tone:

grep -vE '^\s*(#|$)' /opt/cloudimg-radio/cloudimg-radio.liq
settings.log.stdout := true
settings.log.file := false
source_password = environment.get("ICECAST_SOURCE_PASSWORD")
radio = mksafe(amplify(0.3, sine(440.)))
output.icecast(
  %vorbis(quality=0.2),
  host="127.0.0.1",
  port=8000,
  password=source_password,
  mount="/stream.ogg",
  name="cloudimg radio",
  description="Icecast 2.4.4 with Liquidsoap 2.4.6 on Ubuntu 24.04 (generated reference tone)",
  genre="Demo",
  public=false,
  radio)

The service user can check the script at any time; it type checks the whole program without starting it:

sudo -u cloudimg-radio liquidsoap --check /opt/cloudimg-radio/cloudimg-radio.liq && echo SCRIPT_OK
SCRIPT_OK

To play your own music, copy your files to the instance and put them in /srv/cloudimg-radio/music. This build decodes Ogg Vorbis, Opus and FLAC files (it has no MP3 decoder, by design); convert MP3 files first, for example with ffmpeg -i song.mp3 -c:a libvorbis -q:a 5 song.ogg on your own computer.

scp -i <your-key.pem> *.ogg ubuntu@<public-ip>:/tmp/
ssh -i <your-key.pem> ubuntu@<public-ip> 'sudo install -m 0644 /tmp/*.ogg /srv/cloudimg-radio/music/ && rm -f /tmp/*.ogg'

Then edit the script with sudo nano /opt/cloudimg-radio/cloudimg-radio.liq and replace the radio = ... line with a shuffled playlist of that folder, keeping the tone as a fallback for when the folder is empty:

radio = fallback(track_sensitive=false, [
  playlist(mode="randomize", reload_mode="watch", "/srv/cloudimg-radio/music"),
  amplify(0.3, sine(440.))
])
radio = mksafe(radio)

Keep the output.icecast(...) block as it is: it already sends to the local Icecast with this instance's source password, which Liquidsoap reads from its environment. Check the script as the service user, then apply it:

sudo -u cloudimg-radio liquidsoap --check /opt/cloudimg-radio/cloudimg-radio.liq
sudo systemctl restart cloudimg-radio
journalctl -u cloudimg-radio -n 20 --no-pager

reload_mode="watch" picks up files you add to the folder later without a restart. The Liquidsoap 2.4 documentation covers scheduling, crossfades, jingles, live input and loudness normalisation. To serve a second format, add another output.icecast(...) with, for example, %opus and mount="/stream.opus".

Step 8: Broadcast live from your own computer

A live source client runs on your machine and pushes audio to its own mountpoint, alongside the Liquidsoap stream. Use the values from Step 4 and switch TLS (or SSL) on:

Server / host : ICECAST_SOURCE_HOST   (or your DNS name from Step 10)
Port          : 8443   with TLS / SSL enabled
Mountpoint    : /live.ogg   (or /live.mp3, /live.opus)
Username      : source
Password      : the ICECAST_SOURCE_PASSWORD value
Format        : Ogg Vorbis, Opus or MP3
  • butt (Broadcast Using This Tool): add an Icecast server, port 8443, and tick Use TLS. Accept the certificate when butt asks, or install your own certificate (Step 10).
  • Mixxx: in Preferences > Live Broadcasting, type Icecast 2, port 8443, TLS on.
  • ffmpeg, for example to stream a file on a loop:
ffmpeg -re -stream_loop -1 -i show.ogg -c:a libvorbis -q:a 4 -f ogg \
  -method PUT -content_type audio/ogg -chunked_post 0 -send_expect_100 0 -auth_type basic \
  -tls_verify 0 "https://source:<ICECAST_SOURCE_PASSWORD>@<ICECAST_SOURCE_HOST>:8443/live.ogg"

Listeners tune in to a live show at https://<your-host>/live.ogg. The Liquidsoap stream on /stream.ogg keeps running alongside it.

Step 9: Use the status page and the admin interface

Open ICECAST_URL in your browser. The certificate is self signed, so your browser asks you to confirm it the first time (or install your own, Step 10). The public status page lists every active mountpoint with its stream name, description, content type, listener count and current track, with M3U and XSPF playlist links:

The Icecast status page showing the /stream.ogg mountpoint fed by Liquidsoap, named cloudimg radio, content type application/ogg, and the M3U and XSPF playlist links

Open ICECAST_ADMIN_URL and sign in as admin with the admin password. Admin Home shows the global server statistics, including the configured host and the server version:

The Icecast admin home page showing global server statistics, including the host name, the source count and server id Icecast 2.4.4

Mountpoint List shows each mountpoint with its controls: List Clients, Move Listeners to move an audience to another mountpoint without dropping it, Update Metadata to change the track title listeners see, and Kill Source to disconnect a source (Liquidsoap reconnects on its own a few seconds later):

The Icecast admin mountpoint list with the /stream.ogg mountpoint and the List Clients, Move Listeners, Update Metadata and Kill Source controls

List Clients shows each connected listener with a Kick action. Because listeners reach Icecast through nginx, the IP column shows 127.0.0.1; the real listener addresses are in /var/log/nginx/access.log, and source connections on port 8443 are logged in /var/log/nginx/icecast-source.log.

The Icecast admin listener list for /stream.ogg with one client, its connected time, user agent and a Kick action

Step 10: Use your own DNS name and certificate

Point a DNS record at the instance (an Elastic IP keeps the address stable), then tell the instance its name. This regenerates the self signed certificate for that name, updates the stream URLs Icecast advertises and the credentials file, and reloads nginx and Icecast:

sudo icecast-liquidsoap-set-hostname radio.your-domain.example

To use a certificate from a public CA instead, replace /etc/ssl/icecast-liquidsoap/icecast.crt (full chain) and /etc/ssl/icecast-liquidsoap/icecast.key (mode 0600) and run sudo systemctl reload nginx. The same certificate serves port 443 and port 8443.

Step 11: Security checks

The upstream default password is nowhere in the configuration, Icecast listens only on loopback, and Icecast and Liquidsoap cannot start without the first boot passwords:

sudo grep -c "hack""me" /etc/icecast2/icecast.xml /opt/cloudimg-radio/cloudimg-radio.liq || echo "(no match in either file)"
sudo grep -E '<(port|bind-address)>' /etc/icecast2/icecast.xml
systemctl show icecast2 cloudimg-radio -p Requires --value
ps -o user:16=,comm= -C liquidsoap
/etc/icecast2/icecast.xml:0
/opt/cloudimg-radio/cloudimg-radio.liq:0
(no match in either file)
        <port>8000</port>
        <bind-address>127.0.0.1</bind-address>
icecast-liquidsoap-firstboot.service system.slice -.mount sysinit.target

system.slice -.mount sysinit.target icecast-liquidsoap-firstboot.service
cloudimg-radio   liquidsoap

Icecast, Liquidsoap and nginx are blocked from the instance metadata service, so nothing they fetch (relays, URL authentication, a remote playlist) can reach your instance's credentials:

systemctl show icecast2 cloudimg-radio nginx -p IPAddressDeny --value
fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32

fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32

fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32

Liquidsoap is licence clean: it links no FDK AAC or FFmpeg library, and the build reports those encoders as absent:

ldd /opt/liquidsoap/bin/liquidsoap | grep -ciE 'fdk|libav' || true
grep -E '^ *- *(FDK-AAC|FFmpeg|Vorbis|Opus) *:' /usr/share/doc/liquidsoap-cloudimg/build-config.txt | sort -u
0
   - FDK-AAC           : no (requires fdkaac)
   - FFmpeg            : no (requires ffmpeg)
   - Opus              : yes
   - Vorbis            : yes

Maintenance

  • Icecast configuration: /etc/icecast2/icecast.xml. Apply changes with sudo systemctl restart icecast2. Common changes are raising <clients> in <limits> for a larger audience and adding <mount> sections for per mountpoint limits, fallbacks or authentication. Keep the single listen socket on 127.0.0.1:8000: nginx publishes it, and Icecast refuses to start with any other socket layout.
  • Liquidsoap script: /opt/cloudimg-radio/cloudimg-radio.liq, checked with sudo -u cloudimg-radio liquidsoap --check and applied with sudo systemctl restart cloudimg-radio. Its log is in the journal: journalctl -u cloudimg-radio.
  • Changing the source password: edit <source-password> (12 or more characters) in /etc/icecast2/icecast.xml and ICECAST_SOURCE_PASSWORD in /etc/cloudimg-radio/cloudimg-radio.env to the same value, restart icecast2 and cloudimg-radio, and update /root/icecast-liquidsoap-credentials.txt. The admin and relay passwords live only in icecast.xml. First boot runs only once, so it never overwrites your changes.
  • Logs: Icecast in /var/log/icecast2/, nginx (with the real client addresses) in /var/log/nginx/, Liquidsoap in the journal.
  • Backups: the configuration files and your music folder /srv/cloudimg-radio/music are the only state worth keeping.
  • Security updates: unattended security upgrades are on and cover Icecast, nginx and every library Liquidsoap uses. Reboot when a new kernel is installed. Liquidsoap itself is updated by a new version of this image.

Licences and source code

Icecast is developed by the Xiph.Org Foundation and licensed under the GNU General Public License version 2; its source is available from the Ubuntu archive with apt-get source icecast2. Liquidsoap is developed by the Savonet project and licensed under the GNU General Public License version 2 or later; this build links the GPL version 3 or later ocaml-metadata library, so the Liquidsoap binary on this image is distributed under the GNU General Public License version 3 or later. Its licence text, the exact build configuration and the list of every OCaml package used are in /usr/share/doc/liquidsoap-cloudimg/, and the complete corresponding source, every archive the binary was built from, is in /usr/local/src/liquidsoap-2.4.6-source/.

Support

cloudimg provides 24/7/365 expert technical support for this image. Contact support@cloudimg.co.uk. This image is provided by cloudimg and is not affiliated with or endorsed by the Xiph.Org Foundation or the Savonet project.