words:

building a better sonos with free software and cheap hardware

When I first heard about Sonos I was impressed. Completely synchronized music playing in every room is something I’ve always wanted, but the price seemed ridiculous, especially if you like the speakers you already own. Most of their products have a speaker integrated into them, forcing you to ditch any Hi-Fi equipment you might already own. Also I like keeping my own library of digital music and Sonos steers users towards using streaming services.1

For a while I was convinced there wasn’t a good open-source solution that could rival Sonos, but then I started playing with forked-daapd. It’s like iTunes but better. It scans your music library and allows you to stream it to Airplay receivers around the house. It also accepts audio via pipe so you can hook it up to receive audio from other sources and send it to your speakers.

For receivers, Volumio is a great OS that you can install on a Raspberry Pi. It has Airplay reception built in, with plugins for Spotify and other services. This combined with hardware from HiFiBerry gets you a high-quality output in the format of your choice for under $100.

For example I bought some cheap but awesome Edifier speakers for our home office and these are connected via an optical cable to a HiFiBerry Digi+ on a Raspberry Pi 3.

In the living room I have an older Onkyo TR-NX414 receiver that receives audio from via the optical out on a Behringer UCA202 connected to an Intel NUC. Eventually I’ll upgrade the receiver to something that has Airplay built in, but the point is that with DIY-ing this yourself you have the flexibility to use whatever hardware you have. My dad’s old beat-up but otherwise great-sounding Bose 301s still find use in the garage with this system.

intalling forked-daapd

My server is an old PC running Ubuntu which runs everything to do with home automation and home audio with Docker. My full config for this machine is kept here. The relevant piece is the daapd directory:

daapd
├── Dockerfile
├── forked-daapd.conf
└── start.sh

I couldn’t find a good pre-built Docker image for forked-daapd but writing my own was pretty simple. To set this up copy the daapd directory and add a docker-compose.yml with the following:

docker-compose.yml#L97-L109 ↗︎
  daapd:
    container_name: daapd
    build: daapd
    network_mode: host
    volumes:
      - /mnt/pool/tunes:/mnt/pool/tunes:ro
      - ./daapd/forked-daapd.conf:/etc/forked-daapd.conf
    environment:
      - MEDIA_SERVER_NAME=Home
      - PUID=1000
    env_file:
      - ./daapd.env
    restart: always

Be sure to adjust the /mnt/pool/tunes volume to where your music is stored. You can reference the forked-daapd.conf in the project repo for its available options and their defaults.

streaming spotify around the house

Despite what I think about streaming music sometimes you just want to throw Spotify on and have it play through the whole house during a party or when you can’t be bothered to queue up a bunch of music manually. Thankfully there’s the librespot2 open source client which you can pipe right into forked-daapd. One thing that isn’t built in is metadata, which is just a nice thing to have instead of blank album art and empty current track info. A PR was opened but wasn’t accepted for some reason, so I keep a fork of librespot with this feature added.

Like forked-daapd, copy the contents of the librespot folder and add the following to your docker-compose.yml:

docker-compose.yml#L71-L81 ↗︎
  spotify:
    container_name: spotify
    restart: always
    build: librespot
    depends_on:
      - daapd
    volumes:
      - /mnt/pool/tunes/spotify:/data
    env_file:
      - ./librespot.env
    restart: always

Again, update /mnt/pool/tunes to point to your music. You will also need to create a librespot.env file with your Spotify username, password and a name for the streaming destination.

SPOTIFY_NAME=Home
SPOTIFY_USER=myusername
SPOTIFY_PASSWORD=mypassword
'Home' as a destination in the Spotify app

streaming other audio from macs and phones

Sometimes you need audio from places that aren’t your library or Spotify, like playing from the Bandcamp app, website or from YouTube. Luckily there’s also an open-source Airplay receiver called shairport-sync3 we can pipe into forked-daapd.

The setup for shairport-sync is a lot simpler, just add the following to your docker-compose.yml:

docker-compose.yml#L83-L95 ↗︎
  shairport:
    container_name: shairport
    image: kevineye/shairport-sync
    network_mode: host
    depends_on:
      - daapd
    volumes:
      - /mnt/pool/tunes/airplay:/output
      - /mnt/pool/tunes/airplay.metadata:/tmp/shairport-sync-metadata
    environment:
      - AIRPLAY_NAME=Home
    command: --metadata-pipename=/tmp/shairport-sync-metadata -o pipe -- /output
    restart: always
'Home' as an Airplay Output Device on Mac OS

running it

Start Docker with sudo docker-compose up -d and enjoy your more-capable, cheap, whole-home audio system.

I learned alot about running all this stuff from Benoit Beauchamp’s excellent post about setting up his own whole-home music setup, which differs a bit with the use of mopidy as a music manager, but everything else is the same. This video by James Petersen gave me some cool ideas about how to control the system from Home Assistant.


  1. You can add local music via an app running on a computer, but it’s limited to 65,000 tracks. Amateur hour. 

  2. This is also the software the Volumio devices use for Spotify streaming under the hood 

  3. This is also the software the Volumio devices use for Airplay reception under the hood. Everything works together.