⚘ April Chu
𞠕 Last updated on 11.30.2023
When you liberate programming from the requirement to be professional and scalable, it becomes a different activity altogether, just as cooking at home is really nothing like cooking in a commercial kitchen. I can report to you: not only is this different activity rewarding in almost exactly the same way that cooking for someone you love is rewarding, there’s another feeling, too, specific to this realm…What is this feeling? Independence? Security? Sovereignty?
𞠕 Robin Sloan, An App Can Be A Home-cooked Meal
This manual is not a technical tutorial to building a reliable and scalable online radio. It is not intended to be an authoritative or exhaustive reference.
Instead, this guide is an invitation to build your own “home-cooked” radio. This manual will walk through how to assemble free and open source software on a Raspberry Pi to stream audio online. It covers the construction of a radio within a local network and offers steps to make it public (if and when you choose to do so). It is for those who have no experience with servers and the command line, for people who have even the slightest interest in building or understanding networks and infrastructure.
This guide aims to serve as a resource to those seeking to participate as collaborators, and not mere consumers, in our increasingly technical world. This guide aligns with notions of liberating technological creation from industrial hands. I hope that the act of creating handmade platforms built by and for us, we can begin to re-enchant our digital realms. Even more so, I hope that you will begin to engage with new feelings of independence, security, and sovereignty.
Before we begin building the radio, let’s take a step back and look at ① the parts of a network and ② the Internet radio toolchain.
A “local network” refers to a network within a scoped geographic area like a home or office. Devices within a local network can communicate directly with each other without the Internet using local IP addresses.
Internet Service Provider (ISP): The company that provides your local network with an Internet connection.
Local Network Devices: The devices connected to your local network such as computers, smartphones, and IoT (Internet of Things) devices. The Raspberry Pi is a server within the local network, hosting various services like a web and radio server.
Networking Equipment
Network Services
Most of the data that flows in and out of your home is managed automatically by the network devices.
Let’s picture a Raspberry Pi connected to your home’s local network.
For all outbound traffic from the Pi, like streaming music online, the router assigns a private IP to the Raspberry Pi and uses NAT to replace the private address with the router’s public IP. The request is forwarded to the Internet, traveling through the modem to your ISP to the target server. The target server processes the request, generates a response, and sends it back through the same path. Your router uses NAT to direct the response back to the Pi by replacing its public IP with the Pi’s private IP.
Inbound traffic, such as accessing a website hosted on the Pi, first reaches the router’s public IP address. The router uses NAT to forward the requests to the Raspberry Pi’s local IP address and the appropriate port. The Pi processes the request and sends an appropriate response back through the router. The responses traverse the modem and reach the device that initiated the request.
Beyond the automatic processes handled by the networking equipment, there are additional actions needed to secure external access to services hosted on the Pi:
Online radio is usually composed of three parts:
For this project, Broadcast Using This Tool (BUTT) is the stream generator. It captures and encodes audio to send to the Icecast server. Icecast is the streaming media server hosted on the Pi that distributes the stream from the Pi to listeners. Listeners can use the stream’s web page as the media player to tune in to the broadcast.
Additionally, we will be using a virtual audio cable to connect output from VLC to BUTT. This will allow us to stream pre-recorded media as well as broadcast live input at the some time.
You will need a Raspberry Pi and peripherals to interact with the computer:
I have not included any audio equipment. For the purposes of testing out your radio, having a computer with a microphone and speakers is sufficient. You can get additional equipment for the Pi if you want to broadcast from the Pi.
This guide prioritizes open source software. The following software are available to download and use for free:
The Raspberry Pi website has great documentation on how to set up your Pi out of the box. We will mostly be interfacing with the Raspberry Pi through the terminal in this guide.
Common Terminal Commands
sudo: Stands for "Super User Do." Allows user to execute commands with elevated privileges.apt: Stands for "Advanced Package Tool." A package management system install and remove software packages.systemctl: Manages services and provides commands to start, stop, restart, enable, disable, and view status.nano: Opens up a lightweight text editor within the terminal.
First, install the Apache web server. We need to set up a web server to use with Icecast, the streaming media server. This enables us to access Icecast’s web-based administration interface and host a webpage.
sudo apt update
sudo apt upgradesudo apt install apache2Y for “Yes” and hit Entersudo systemctl start apache2sudo systemctl enable apache2http://localhost/ on the Pi’s browser or enter the Pi’s IP address on another computer on the local network. You should see the Apache 2 default welcome page.hostname -IInstall Icecast2, the media streaming server.
sudo apt install icecast2 -yYes on the pop-up that asks if you want to configure Icecast. Don’t change anything, just hit Enter.sudo nano /etc/icecast2/icecast.xmlSource Password (used for the source client authentication) and Admin Password (used for authenticating admin features of Icecast) for security. You can read the Icecast Documentation to learn more about basic setup.systemctl restart icecast2
systemctl status icecast2http://192.168.0.34:8000/ (use the IP address of your Pi) to see the status page of your stream. There will be nothing under “Server Status” because you have not started a stream yet. We have to install BUTT or a stream generator software to begin broadcasting. 
Download and configure BUTT, the stream generator.
Download Location
You can download BUTT on your Pi or your computer, depending on where you want to stream audio from. I downloaded it on both but remember that if you use your Pi to stream audio you need a mic for audio input and speakers or headphones for output.
Before installing anything, update and upgrade the system
sudo apt-get update
sudo apt-get upgradeChoose and download the appropriate source package for your OS from the BUTT site and follow the installation directions on the manual.
Preferences > Add/Remove Software.butt. Check the box next to the highlighted package and click Apply. This will install the butt.Sound & Video you should see butt as an available application.Open BUTT from terminal or by opening the application from the GUI.
buttGo to Settings > Main > Server Settings > Add to add a server and link your BUTT broadcast to your Icecast2 stream.

localhost or the IP address of your Pi8000 is the default from Icecast2 but use the number at <listen-socket> in the Icecast2 configuration file<source-password>streamsourceHit the Play button on BUTT to begin the stream
http://localhost:8000/stream or replace localhost with the IP address of your Pihttp://localhost:8000/stream, make sure you have configured the input device on BUTT. Click on Settings, go to the Audio tab, and choose a device under Primary Audio Device.We're local!
We now have a working local radio. Now you should be able to hear your broadcast from
http://localhost:8000/stream. The radio can only be accessed from devices connected to the same local network and not available publicly on the Internet. We have to make some configurations so that listeners can stream the broadcast.
If you want to stream pre-recorded media, like a playlist, you should connect VLC to BUTT with a virtual audio cable (VAC).
Download VLC from the website. Make sure to choose the appropriate package for your operating system.
Download VB-Cable VAC from the website. This is currently only available on Windows and MacOS. For Linux, try Jack Audio Connection Kit (JACK).
Latency to 7168 smp. This will help synchronize the audio from VLC to the Icecast stream so we can hear the media from VLC on the stream.
Open VLC and upload some media to the Playlist. Go to Audio > Audio Device and choose VB-Cable or the name of the VAC you are using. This will output the media from VLC to the VAC.
Open BUTT and go to Settings > Audio to change the input devices.
Macbook Microphone as the Primary Audio Device and VB-Cable as the Secondary Audio Device. This allows me to stream the media from VLC and my microphone input at the same time. You can set VB-Cable as the only audio device if you only want to stream from VLC.Verify the media is streaming from VLC
http://localhost:8000/stream. You should be able to hear the media from VLC.
You might find it helpful to set up SSH on your Pi so you can access the computer remotely. SSH lets you control the Pi from your laptop without setting up a monitor.
Follow this guide from Tom's Hardware.
Make sure you know our username and password for the Pi. To change the default password:
passwdEnable SSH
Option 01: Enable SSH through the Pi desktop
Preferences > Raspberry Pi ConfigurationInterfaces tab and toggle on SSHOption 02: Enable SSH from the terminal
sudo raspi-config
Select Interface Options

Select SSH. Then choose Yes when asked to enable SSH. Hit Enter on the confirmation box.

Make sure you select Finish at the end
Check if SSH is running. If not, start the SSH service.
# Check SSH status
sudo service ssh status
# Start SSH
sudo service ssh startDisable Root Login
Disabling direct root login enhances the security of your system by reducing the risk of unauthorized access. It's recommended to log in as a regular user and then use
sudoto perform administrative tasks.
nano /etc/ssh/sshd_config#PermitRootLogin prohibit-password#PermitRootLogin nosudo systemctl restart sshdNow we should be able to login from another computer on the same network!
ssh username@[address]username and address with your username and Pi’s IP address (ex ssh april@192.168.0.20).username@rpi:~ at the beginning of the line in your terminal.To exit out of SSH use:
exitOnce your radio is working locally, we can set up port forwarding and dynamic DNS to make sure your radio is available publicly.
If your Raspberry Pi is connected to a router, you’ll need to set up port forwarding to forward external traffic to your Raspberry Pi’s local IP address. I set up port forwarding on 8000 (for Icecast), port 80 (for HTTP), port 22 (for SSH), and port 443 (for HTTPS).
192.168.0.1 or 192.168.1.1. If you’re not sure, check your router’s documentation or your ISP. My router had a label on the bottom of the device with the address and settings password.| Service | Internal IP Address | Internal Port | External Port | Protocol |
|---|---|---|---|---|
| Icecast | Pi’s Local IP | 8000 | 8000 | Both (TCP & UDP) |
| SSH | Pi’s Local IP | 22 | 22 | TCP |
| HTTP | Pi’s Local IP | 80 | 80 | TCP |
| HTTPS | Pi’s Local IP | 443 | 443 | TCP |
- If your router settings uses port ranges (ex: local start port, local end port, external start port, external end port), just set them all to the same port number.
- Port 8000 is the default port for Icecast. If you changed the port number in your Icecast configuration file, use the port that you specified. This should also be the same port you used in your BUTT settings.
The public IP address assigned to your router is often dynamic, meaning it can change periodically due to actions by your Internet Service Provider (ISP) or when you switch networks. By configuring a Dynamic DNS, you can associate a domain name with your router’s changing public IP address. This makes it easier to maintain the access to your stream.
I provide steps to register with two free DDNS providers, No-IP and DuckDNS. No-IP requires you to manually confirm that the hostname is in use every 30 days while DuckDNS does not require any verification. I found that I ran into more issues with network security using a DuckDNS hostname which is why I switched over to No-IP.
No-IP will send you and email every 30 days to confirm that the hostname is still in use. If you do not confirm the hostname is active, your hostname will be deleted.
Register with No-IP
Dynamic DNS tab > No-IP Hostname. If you created a hostname, you should be able to see your hostname on the dashboard. If not, create a hostname.IP/ Target and make sure the Hostname Type is DNS Hostname A. This will map your hostname to the public IP address of your router, which forwards traffic to your Pi.Configure DDNS on the Raspberry Pi
Follow No-IP's official documentation.
wget https://dmej8g5cpdyqd.cloudfront.net/downloads/noip-duc_3.0.0-beta.7.tar.gztar xf noip-duc_3.0.0-beta.7.tar.gzcd /home/$USER/noip-duc_3.0.0-beta.7/binaries && sudo apt install ./noip-duc_3.0.0-beta.7_armhf.debnoip-duc -g name.ddns.net -u username -p passwordname.ddns.net with your hostname.username and password with your email and account password. If your password uses special characters, put it in single quotes (ex: '!Password') to prevent errors.Verify DDNS by entering the domain you just set up (ex http://radio.ddns.net) into a web browser. You should see the same web page as when you use your router’s public IP.
Register with Duck DNS
Create an account on Duck DNS or sign in with an existing account.
Once you are logged in, you will see the section to add a domain. Choose a subdomain name and click Add Domain.

You should your router’s public IP address automatically filled next the domain name. You can find your router’s public IP address by through an online IP address checker.
Configure DDNS with the Raspberry Pi
Follow the Duck DNS official documentation. Choose
pias the operating system.
SSH into your Pi or open the terminal on your Pi. We will be setting up a script to intermittently check the router’s IP address and to update DuckDNS with any changes.
Make a directory for duckdns, and create the script in the directory.
mkdir duckdns
cd duckdns
nano duck.shIn the duck.sh document, add the follow line.
echo url="https://www.duckdns.org/update?domains=YOUR_DOMAIN&token=YOUR_TOKEN&ip=" | curl -k -o ~/duckdns/duck.log -K -
Replace YOUR_DOMAIN with the DuckDNS subdomain associated with your Pi (ex http://radio.duckdns.org).
Replace YOUR_TOKEN with the token provided on your DuckDNS account page.

The API will detect the IP address automatically so there is no need to enter an IP address.
Save the file (enter ctrl + X to exit and enter Y to save).
Make the script file executable:
chmod 700 duck.shThen using cron, we will make the script run every 5 minutes. First, open cron:
crontab -e
*/5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1ctrl + X to exit and enter Y to save).You can verify the script is running if the following comand returns a prompt.
./duck.shYou can also check the log to see if the last attempt was successful (returns OK).
cat duck.logVerify DDNS by entering the domain you just set up (ex http://radio.duckdns.org) into a web browser. You should see the same web page as when you use your router’s public IP.
A firewall is a security system that enhances the security of the Pi by regulating the flow of traffic between the Pi and local network. We have to update the Pi’s firewall rules to allow traffic at specific ports. iptables is the command-line utility uses on a Pi to define the rules for network traffic.
Check if iptables is installed from the terminal
sudo iptables --version
iptablessudo apt-get update
sudo apt-get install iptablesAllow the four ports for which we have set up port forwarding.
sudo iptables -A INPUT -p tcp --dport 8000 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPTSave and restart iptables
sudo service iptables save
sudo service iptables restartCheck the current firewall rules. The output should list the four ports above.
sudo iptables -LWe're public!
From a device outside of the local network that your Pi is connected to, you can access your stream from
http://public-ip:8000/stream(replacepublic-ipwith your router's Public IP address) orhttp://radio.ddns.org:8000/stream. Make sure you are streaming from BUTT or there will be an 404 error.
A downside with dynamic DNS hostnames is that they often get blocked by enterprise wifi networks (like school and company networks). This means blocked access to the stream.
One workaround is to buy a custom domain and connect the DDNS hostname to the domain. Using a custom domain associated with a DDNS hostname is not guaranteed to work in all situations, as network administrators can employ various methods to control access. Some domain providers like namecheap.com offer free DDNS services with domain registration so you don’t have to use third-party DDNS services.
Purchase a domain on Namecheap
Configure Dynamic DNS on the domain in Namecheap
Enable Dynamic DNS for the domain
Domain List, click on Manage > Advanced DNS > Enable the Dynamic DNS toggle.Dynamic DNS Password. You will need this later when configuring the Pi.Create A+Dynamic DNS records. This associates an IP Address with the domain name.
Click on Manage for the domain. On the Advanced DNS tab, go to Host records.
Click on Add New Record.
If you want your stream to be accessed at your-domain.com and www.your-domain.com, add the following records.
your-domain.com)A+ Dynamic DNS Record@127.0.0.1*www.your-domain.com)A+ Dynamic DNS Recordwww127.0.0.1*I have main website deployed on Vercel on my base domain and the stream is only accessible on the subdomain stream.domain.com. To set up the record on a subdomain:
stream.your-domain.com)A+ Dynamic DNS Recordstream127.0.0.1*You can use any IP address for
Value. Once the DDNS client is configured, the IP address will be automatically updated.
Configure DDNS on Raspberry Pi
Like we did above when we set up DDNS, we have to configure a client to update the A record on Namecheap to correct the public IP address.
From the Pi’s terminal or via SSH, update packages and install ddclient:
sudo apt-get update
sudo apt-get install ddclient
Open the configuration file using nano:
sudo nano /etc/ddclient.confYou will see the answers to the installation pop-up. Replace the file’s content:
use=web, web=dynamicdns.park-your-domain.com/getip
protocol=namecheap
server=dynamicdns.park-your-domain.com
login=[your-domain.com]
password=[your namecheap dyndns password]
[subdomain]
[your-domain.com] with your domain nameAdvanced DNS@ to update the base domain, www for www.domain.com or subdomain for subdomain.domain.com.Exit (CTRL + X)and save the file (Hit Y)
Setup the client to run at startup
sudo systemctl start ddclient.serviceUpdate Icecast configuration
We have to update the Icecast configuration with the new hostname
Before making changes, create a backup of the Icecast configuration file
# Navigate to configuration file directory
cd /etc/icecast2/
# Create a backup of the configuration file
sudo cp icecast.xml icecast.xml.bakOpen the configuration file using nano:
sudo nano /etc/icecast2/icecast.xmlUpdate the hostname tag with your domain. If you set it up on your subdomain (stream.your-domain.com), then use the subdomain.
<hostname>your-domain.com</hostname>Exit (CTRL + X)and save the file (Hit Y)
Restart Icecast
sudo service icecast2 restartNow if you go to your-domain.com or stream.subdomain.com, you should see the Icecast status page.
Update BUTT
Settings. On the Main tab, go to Server Settings and click on EditAddress with your domain (or subdomain). Hit Save.your-domain.com/stream or stream.your-domain.com/stream.Now that you have a domain, it’s a good idea to set up SSL. When your site has a HTTPS padlock, it enhances security, ensures encryption, and reduces the likelihood of browsers blocking it.
Install Cerbot, a client for Let’s Encrypt and a free Certificate Authority. From the terminal, run:
sudo apt-get update
sudo apt-get install certbotObtain an SSL certificate for the domain.
sudo certbot certonly --standalone -d your-domain.com
your-domain.com with your actual domain.Check the certificate for your domain is listed when you run:
sudo certbot certificates
Configure automatic renewal
sudo certbot renew --dry-runcrontab to add a job.sudo crontab -e0 */12 * * * certbot renewsudo nano /etc/icecast2/icecast.xml<!-- Enable SSL -->
<ssl>1</ssl>
<ssl-certificate>/etc/letsencrypt/live/[your-domain.com]/fullchain.pem<ssl-certificate>
<ssl-private-key>/etc/letsencrypt/live/[your-domain.com]/privkey.pem<ssl-private-key>
<port>443</port>[your-domain.com] for both the <ssl-certificate> and <ssl-private-key> with the your actual domain.CTRL + X) and save the changes (Y)sudo service icecast2 restartBroadcast Using This Tool (BUTT)
Domain Name System (DNS)
Dynamic DNS (DDNS)
Dynamic Host Configuration Protocol (DHCP)
Internet Service Provider (ISP)
IP Address
Modem
Network Address Translation (NAT)
Ports
Radio Server
Raspberry Pi
Router
Secure Shell (SSH)
Secure Sockets Layer (SSL)
Server
Web Server
Virtual Audio Cable (VAC)