My personal, self-hosted music streaming setup (mpd + icecast)

Gemini version of this post

I have been exploring and switching to free (as in freedom), self-hosted alternatives to proprietary services, in order to escape surveillance capitalism and stop relying on third-parties as much as possible. I have made a lot of switches, but Spotify seems to be almost irreplaceable, it’s simply the best music streaming service… but what if I want to own my music and also have real, non-AI-generated shuffle playback?

The solution might be obvious: download your music and sync it between your devices; but that’s boring. I want to be able to stream and share my music easily, using entirely free software. I want my own Spotify. So, the first option I considered was Funkwhale, but since I will be illegally hosting copyrighted music, the federation might not be a good idea. Maybe I can disable federation, but I still wanted to try something more personal and private.

I came across Ampache, which is pretty old and mature, and even though it is written in PHP, it seems to support a lot of features and streaming standards, so I decided to go with it. I managed to get Ampache working after following the installation instructions. My first impression was that the UI looks a bit old, and I thought I would be able to upload music using the web UI, but apparently not: I need to add it to a folder in the filesystem and tell Ampache where to search for music. I would have done so, but the bad and cluttered UI/UX discouraged me from even trying. Besides, I don’t like PHP that much (even though Laravel is the framework I’m most proficient on for backend development).

After destroying the whole Ampache installation, Jellyfin caught my eyes, so I decided to give it a try. Jellyfin runs on the .NET platform, and it is free software. I installed it via Docker. Jellyfin also required me to add music directly into the filesystem, so I added a couple of songs, and they played! I installed the official iOS app, which basically embeds the web UI and WebKit has limited support for media formats. I found out transcoding wasn’t working, so the app couldn’t play my music, most of which is on FLAC format; but at this point, Jellyfin started to seem kinda not what I wanted: it is a general multimedia server, I wanted something exclusively intended for music.

After destroying the whole Jellyfin installation, I was very exhausted. I took a break, grabbed some snacks, and after a while I came back to keep searching, this time for something more minimal. I caught my old Orange Pi One watching me, pleading to be used, pointing out that self-hosting in AWS servers misses the whole point of self-hosting and decentralisation, and self-sufficiency. So, I flashed Armbian to my old microSD card, inserted it into my dusty little board, and started working on it. I needed something that an armv7 Allwinner H3 could handle, so I came across the good old mpd (music player daemon), written in C++ and with its own network protocol for playback. I actually came across mpd before I even considered using my Pi, but I failed to find the build dependencies in Amazon Linux 2 and the CentOS community repositories (can they please decide whether using the lib- prefix or not?)

I installed WireGuard, Yggdrasil and configured mpd. mpd.conf is pretty straightforward, but I had to figure out some stuff, including the audio output, which was set to ALSA, but I would stream anyway, so I thought it wouldn’t matter. I got mpd running, added a couple of songs into the configured mountpoint, and installed Persephone in macOS to start streaming the music! Oh, surprise, no sound in Persephone, and the volume control was disabled. I found I had to set the ALSA output to “software” instead of “hardware” in order for the control to work, but then it started seeming weird. Now the volume control was enabled, but still no sound. Oh, surprise! Turns out mpd was designed to play music on the device and not actually streaming it over the Internet. The mpd protocol only provides remote control for playback, but not actually streaming capabilities.

I was about to give up with mpd, but I found you can use IceCast2 to stream the music over HTTP! I installed it, configured it, and asked mpd to use my icecast2 server as an audio output. However, that means now I had to use two separate programs/apps to control and stream; but that’s actually cool, since I can use one device as remote control and another for streaming. I can even use it as a Jukebox if I share the HTTP stream with friends. PERSONAL SPOTIFY ACHIEVED! Not really. :(

Now I had to solve the storage problem: the microSD card is old and only has 7GB. I decided to mount a USB in a directory where mpd can access it, so I can download the music directly and then plug it into the Sp-OPi-fy. Another problem is that using two separate apps on iOS is not practical, and also the lack of offline listening. It would be nice to have a cross-platform app designed for this particular Spotify-like use-case, one that combines mpd + HTTP streaming + offline listening [PROJECT IDEA!] Until I can build an app like this, I won’t replace Spotify entirely.

I would turn this post into a tutorial, but I will link to the nice tutorial I followed instead, turns out this setup of mpd + icecast + Pi is relatively popular.

Update 2021-02-24

Few weeks ago, I found this setup worked better as a personal internet radio than a failed attempt of a Spotify clone. Also, I decided not to keep it to myself, but to share it with others, and Ávalos’ Indie Radio was born. From time to time, I do streams and announce them on Mastodon.