Ansible playbook to deploy/manage Raspberry Pi(s) + Hifiberry DAC HAT(s) with upmpdcli, MPD and Snapcast as headless multi-room UPnP targets
I wrote this because the number of pis in my family has reached a point where deploying them and keeping them up to date has become a chore.
The pis are mainly used as UPnP casting targets for Symfonium on android via wifi.
upmpdcli and MPD allow for bit perfect casting while Snapcast allows for multi-room casting (letting multiple pis play the same music in sync).
This playbook can turn a pi (or many) with a fresh Raspberry Pi OS (Lite) install into a finished headless UPnP target within a couple of minutes with no user interaction (Pi 3B took 12 minutes).
I tested it on Raspberry Pi Zero 1W, Zero 2W, 2 and 3B. Tested DACs: Hifiberry Dac+ Pro and Dac+ Zero.
What can this playbook do?
- update all packages (including those not available on apt)
- create a user with passwordless sudo privileges and add the appropriate ssh key (optional)
- configure the audio settings to use the Hifiberry DAC (optional)
- install mpd and upmpdcli and configure them
- install and configure snapclient and/or snapserver (optional)
Following the guides and documentation on these sites you can achieve the same as this playbook if you perform a bit over 20 steps (per pi).
Hifiberry
MPD - Music Player Daemon
upmpdcli - UPnP Audio Media Renderer based on MPD
Snapcast - Synchronous multiroom audio player
Required:
- ansible must be installed on your PC
- Raspberry Pi OS Lite should be freshly installed (it's what I've tested, it might work with other debian/ubuntu variants)
Download the repository as .zip and unpack it where you want to run the playbook or clone the repository.
This guide assumes that you have a Raspberry Pi, a power supply, a Hifiberry DAC HAT (optional) and a microSD card. Linux commands are used, but they work in WSL on Windows as well.
If you already have a SSH key for your user as well as an ansible specific SSH key, you can skip these steps.
- create an SSH key for your normal user by running
ssh-keygen -t ed25519 -C "user default"
(replace "user" with your chosen username). I recommend to use a password for this key. - create an ansible specific SSH key by running
ssh-keygen -t ed25519 -C "ansible"
and save it as "ansible" (otherwise it will overwrite the first key). For this key I recommend not to use a password. You should then have two SSH key pairs (public/private) in~/.ssh
.
- download and install Raspberry Pi Imager
- connect the microSD card to your PC
- open Raspberry Pi Imager
- under "Choose Device" select the model of Pi you are using
- under "Choose OS" select the OS version you want (I usually choose Raspberry Pi OS Lite (64bit))
- under "Choose Storage" make sure you select the correct microSD card (everything on it will be deleted!)
- Edit Settings → General: add OS customisation settings, I recommend filling out everything (hostname, username, password, wifi config, locale settings)
- Services: Enable SSH, I recommend using public-key authentification only and pasting the contents of the public key of your normal user
cat ~/.ssh/id_ed25519.pub
- save changes, apply the OS settings and confirm flashing the microSD card
Since the playbook will configure the pis based on their IP/hostname, giving them static IPs is recommended.
- install the microSD card in your pi, install the Hifiberry DAC HAT (optional) and connect the power supply
- once the pi has booted, find out its IP address in your router
- SSH into the pi via
ssh username@IP/hostname
(replace "username" with the user you set in Raspberry Pi Imager) - if you used an up to date image (bookworm as of writing), run
sudo nmtui
and set a static IP, then reboot the pisudo reboot
Snapcast uses a client/server model where all configured clients play in sync what is cast to the server.
- In the castpi2go directory, edit the inventory
nano inventory
and add the IP/hostname of your pi either under snapclients (installs only snapclient) or under snapservers (installs snapclient and snapserver). - create a host variable file for your pi
cp host_vars/example.yml IP/hostname-of-your-pi.yml
and fill it out: - "friendly_name" is what the pi will be called in the casting options
- "hifiberry_overlay" is specific to your DAC, see the Hifiberry documentation to find the correct one.
- "snapcast_server_ip" this is the IP of the snapserver that the pi will play in sync with. When you cast to the snapserver, all snapclients will play the same in sync.
Note: You can delete the "hifiberry_overlay" line if you only want to install mpd, upmpdcli and snapserver on a pi without a Hifiberry DAC. This will skip the config steps that alter the audio settings of the pi and also will not install snapclient.
bootstrap.yml creates a new user "nandor" with passwordless sudo on the pi that is then used by the castpi2go.yml playbook. This new user uses the "ansible" key created earlier to authenticate. You can use a different user ("ansible" if you lack creativity), I chose nandor because this playbook is relentless.
- In the castpi2go/vars subdirectory, edit the SSH config file
nano ssh_config.yaml
and fill outansible_user_ssh_key: ""
with your public ansible keycat ~/.ssh/ansible.pub
. - If you want to use a different user, replace "nandor" in the first line (
ansible_user: nandor
). - When using a different user, in the castpi2go directory you have to replace "nandor" in the
remote_user = nandor
line in the ansible confignano ansible.cfg
with your chosen ansible user.
- Ensure that "private_key_file" in
nano ansible.cfg
points to your ansible SSH key and that the "remote_user" matches your chosen ansible user. - Execute the bootstrap playbook
ansible-playbook bootstrap.yml -u user --key-file ~/.ssh/id_ed25519
(replace "user" with the user you set in Raspberry Pi Imager and "--key-file" with your normal SSH key).
- Ensure that "private_key_file" in
nano ansible.cfg
points to your ansible SSH key and that the "remote_user" matches your chosen ansible user. - In the castpi2go directory, execute the main playbook
ansible-playbook castpi2go.yml
This can also be used later on to update the pi(s), as it will update all apt packages as well as snapclient/snapserver and upmpdcli which are not available on apt.
If you want to add more pis, repeat the steps to flash the microSD card (Raspberry Pi Imager should remember your credentials and ssh key so you only have to change the host name).
Then give the pi a static IP and add that to the inventory file. Create a matching host_vars file for it and set the variables, then run the bootstrap playbook and the main playbook.
If you do not want to execute the playbooks on all hosts each time (does no harm but may slow down execution), limit them to a specific host like this: ansible-playbook bootstrap.yml -u user --key-file ~/.ssh/id_ed25519 --limit pi-IP/hostname
and ansible-playbook castpi2go.yml --limit pi-IP/hostname
Let's say you have 3 Raspberry Pis:
3B without DAC on 192.168.1.42
3B with Hifiberry Dac+ Pro on 192.168.1.40
Zero 2W with Hifiberry Dac+ Zero on 192.168.1.41
Since one of the 3Bs has no DAC, you want to install the snapserver on it. The config would look like this:
inventory
would contain:
[snapclients]
192.168.1.40
192.168.1.41
[snapservers]
192.168.1.42
host_vars/192.168.1.40.yml
would contain:
friendly_name: Pi3B
hifiberry_overlay: dtoverlay=hifiberry-dac plus
snapcast_server_ip: 192.168.1.42
host_vars/192.168.1.41.yml
would contain:
friendly_name: PiZero2W
hifiberry_overlay: dtoverlay=hifiberry-dac
snapcast_server_ip: 192.168.1.42
host_vars/192.168.1.42.yml
would contain:
friendly_name: Pi3B_snapserver
snapcast_server_ip: 192.168.1.42
This would yield 3 new UPnP casting targets in Symfonium: Pi3B, PiZero2W and Pi3B_snapserver. Casting to Pi3B_snapserver would lead to the other two pis playing in sync. In this case you could control the volume of these 2 pis by opening http://192.168.1.42:1780/
in a browser. Alternatively you could cast to each of the 2 pis individually, in which case Symfonium controls the volume.
For an inexpensive but capable player I suggest using a Raspberry Pi Zero 2W, a Hifiberry Dac+ Zero and a 32GB or 64GB microSD card of class A3 at least. If you have access to a 3D printer, there are also a couple of case designs on Thingiverse that should fit this combo: Example 1, Example 2
I recommend using Symfonium on android with lms as the provider. When the pi(s) are fully set up they should pop up as cast targets in Symfonium. If you want to listen on a specific pi, cast to that. If you want to use multi-room, cast to the snapserver pi. You can group the snapclients and adjust their volume individually or in groups by opening pi-snapcast-IP:1780 in any browser or by using dedicated apps like Snapdroid.