Multi-room audio is one of those things that sounds like it should be simple. You’ve got speakers, you’ve got a network — just pipe audio from A to B, right? Well, not quite. One of our clients wanted whole-house audio using Raspberry Pis as receivers with Snapcast, and while the server side was straightforward, getting snapclient to talk to PulseAudio on a fresh Raspbian Bookworm install turned into a bit of a detour.
The problem
Out of the box, snapclient on Raspbian Bookworm can’t connect to PulseAudio. The service starts, but there’s no sound — and the logs are full of connection refused errors. Since these Pis are headless appliances with no desktop environment, PulseAudio isn’t running in a user session where snapclient can reach it.
Going system-wide
The fix is to run PulseAudio as a system-wide daemon. Not everyone’s favourite approach — the PulseAudio docs will wag a finger at you — but for a headless audio appliance it’s the pragmatic choice.
First, we create a systemd service for PulseAudio:
sudo nano /etc/systemd/system/pulseaudio.service
[Unit]
Description=PulseAudio Daemon
[Install]
WantedBy=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/pulseaudio --system --realtime --disallow-exit --no-cpu-limit
Restart=on-failure
LimitRTPRIO=1000
LimitNICE=-20
LimitMEMLOCK=256M
Making snapclient wait its turn
Next, we update the snapclient service to depend on PulseAudio — otherwise it might start before PulseAudio is ready and sulk about it:
sudo nano /etc/systemd/system/multi-user.target.wants/snapclient.service
[Unit]
Description=Snapcast client
Documentation=man:snapclient(1)
Wants=avahi-daemon.service
After=network-online.target time-sync.target sound.target avahi-daemon.service pulseaudio.service
[Service]
EnvironmentFile=-/etc/default/snapclient
ExecStart=/usr/bin/snapclient --logsink=system $SNAPCLIENT_OPTS
User=snapclient
Group=snapclient
Restart=on-failure
[Install]
WantedBy=multi-user.target
Note the pulseaudio.service added to the After= line.
Permissions and config
The snapclient user needs to be in the pulse-access group to talk to the system-wide PulseAudio daemon:
adduser snapclient pulse-access
And finally, we tell snapclient to actually use PulseAudio as its output:
sudo nano /etc/default/snapclient
START_SNAPCLIENT=true
SNAPCLIENT_OPTS="--player pulse"
Reboot the Pi and that’s it — audio sorted.