I am trying (struggling) to get shairport-sync up and running on my server. At first I had huge ambitions of this elaborate config where I am running two airplay2 enabled instances with my own systemd units and including their configurations and alsa resampling config files all declaratively… but couldn’t get it to work (that’s the setup I had on Debian where I came from).
Anyway after stripping my project back further and further to just at least get it running, I have found that I still can’t. Even with the premade module. Has anyone gotten this working themselves?
The shell of what was once a great project that I still can’t get working:
{ config, pkgs, ... }:
{
# enable pipewire with alsa and pulse support
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
# enable shairport-sync module
services.shairport-sync.enable = true;
services.shairport-sync.openFirewall = true;
}
and the errors I’m getting, at least with this iteration:
Jan 15 09:30:50 morgoth shairport-sync[286202]: 0.000427655 "shairport.c:2285" Startup in classic Airplay (aka "AirPlay 1") mode.
Jan 15 09:30:50 morgoth shairport-sync[286202]: 0.000046585 "shairport.c:2321" Version String: "4.3.5-alac-OpenSSL-Avahi-ALSA-sndio-jack-ao-pa-pw-soundio-stdout-pipe-soxr-convolution-metadata-mqtt-dbus-mpris-sysconfdir:/etc"
Jan 15 09:30:50 morgoth shairport-sync[286202]: 0.000015270 "shairport.c:2340" Command Line: "/nix/store/hqspw09ipa7fgf7c08668dh3rif9ynfs-shairport-sync-4.3.5/bin/shairport-sync -v -o pa".
Jan 15 09:30:50 morgoth shairport-sync[286202]: 0.000007166 "shairport.c:2376" Log Verbosity is 1.
Jan 15 09:30:50 morgoth shairport-sync[286202]: 0.004660177 "audio_pa.c:222" *fatal error: failed to connect to the pulseaudio context -- the error message is "Connection refused".
Jan 15 09:30:50 morgoth shairport-sync[286202]: 0.000028655 "shairport.c:1736" emergency exit
Jan 15 09:30:50 morgoth systemd[1]: shairport-sync.service: Main process exited, code=exited, status=1/FAILURE
I the error gives a failure to connect to pulseaudio, I have tried both pipewire with pulseaudio support, as well as using actual puleaudio and no pipewire. When doing the latter I add the shairport user to the audio
group. Still didn’t work.
And in case anyone is interested in seeing what my original idea was, I’ll include that here. Since I want to run two instances using two usb dacs, I couldn’t use the module.
{ config, pkgs, ... }:
{
# add shairport-sync user
users.users.shairport = {
description = "Shairport user";
isSystemUser = true;
createHome = true;
home = "/var/lib/shairport-sync";
group = "shairport";
extraGroups = [ "pulse-access" ];
};
users.groups.shairport = {};
# open firewall ports
networking.firewall = {
interfaces."enp2s0" = {
allowedTCPPorts = [
3689
5353
5000
];
allowedUDPPorts = [
5353
];
allowedTCPPortRanges = [
{ from = 7000; to = 7001; }
{ from = 32768; to = 60999; }
];
allowedUDPPortRanges = [
{ from = 319; to = 320; }
{ from = 6000; to = 6009; }
{ from = 32768; to = 60999; }
];
};
};
# packages
environment = {
systemPackages = with pkgs; [
alsa-utils
nqptp
shairport-sync-airplay2
];
};
# enable pipewire with alsa support
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
# enable avahi
services.avahi = {
enable = true;
allowInterfaces = [ "enp2s0" ];
};
systemd.services = {
nqptp = {
description = "Network Precision Time Protocol for Shairport Sync";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${pkgs.nqptp}/bin/nqptp";
Restart = "always";
RestartSec = "5s";
};
};
outdoor-speakers = {
description = "Outdoor speakers shairport-sync instance";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "root";
Group = "root";
# ExecStart = "${pkgs.shairport-sync}/bin/shairport-sync -c /srv/shairport-sync/outdoor_speakers.conf";
ExecStart = "${pkgs.shairport-sync}/bin/shairport-sync -v -o alsa";
};
};
# dining-room = {
# description = "Dining room shairport-sync instance";
# wantedBy = [ "multi-user.target" ];
# serviceConfig = {
# User = "root";
# Group = "root";
# ExecStart = "${pkgs.shairport-sync}/bin/shairport-sync -c /srv/shairport-sync/dining_room.conf";
# };
# };
};
}
Well. I ended up getting it all working after all. So if you’re here from google or looking for how to setup a multi instance shairport-sync
nix config, you’re in the right place.
{ config, pkgs, lib, ... }:
{
# user and group
users = {
users.shairport = {
description = "Shairport user";
isSystemUser = true;
createHome = true;
home = "/var/lib/shairport-sync";
group = "shairport";
extraGroups = [ "pulse-access" ];
};
groups.shairport = {};
};
# open firewall ports
networking.firewall = {
interfaces."enp2s0" = {
allowedTCPPorts = [
3689
5353
5000
];
allowedUDPPorts = [
5353
];
allowedTCPPortRanges = [
{ from = 7000; to = 7001; }
{ from = 32768; to = 60999; }
];
allowedUDPPortRanges = [
{ from = 319; to = 320; }
{ from = 6000; to = 6009; }
{ from = 32768; to = 60999; }
];
};
};
# packages
environment = {
systemPackages = with pkgs; [
alsa-utils
nqptp
shairport-sync-airplay2
];
};
# enable pulseaudio
services.pipewire.enable = false;
hardware = {
pulseaudio = {
enable = true;
support32Bit = true;
systemWide = true;
};
};
# enable Avahi
services.avahi = {
enable = true;
publish.enable = true;
publish.userServices = true;
allowInterfaces = [ "enp2s0" ];
};
# systemd services
systemd.services = {
nqptp = {
description = "Network Precision Time Protocol for Shairport Sync";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${pkgs.nqptp}/bin/nqptp";
Restart = "always";
RestartSec = "5s";
};
};
dining-room = {
description = "Dining room speakers shairport-sync instance";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "avahi-daemon.service" ];
serviceConfig = {
User = "shairport";
Group = "shairport";
ExecStart = "${pkgs.shairport-sync-airplay2}/bin/shairport-sync -c /etc/dining_room.conf";
Restart = "on-failure";
RuntimeDirectory = "shairport-sync";
};
};
outdoor-speakers = {
description = "Outdoor speakers shairport-sync instance";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "avahi-daemon.service" ];
serviceConfig = {
User = "shairport";
Group = "shairport";
ExecStart = "${pkgs.shairport-sync-airplay2}/bin/shairport-sync -c /etc/outdoor_speakers.conf";
Restart = "on-failure";
RuntimeDirectory = "shairport-sync";
};
};
};
# write shairport-sync configs
environment.etc."dining_room.conf".text = ''
general =
{
name = "Dining Room";
output_backend = "pa";
port = 7000;
airplay_device_id_offset = 0;
};
pa =
{
sink = "alsa_output.usb-Generic_USB_Audio_20210726905926-00.analog-stereo";
};
'';
environment.etc."outdoor_speakers.conf".text = ''
general =
{
name = "Outdoor Speakers";
output_backend = "pa";
port = 7001;
airplay_device_id_offset = 1;
};
pa =
{
sink = "alsa_output.usb-Generic_USB_Audio_20210726905926-00.analog-stereo.2";
};
'';
}
# run `sudo -u pulse PULSE_RUNTIME_PATH=/run/pulse pactl list sinks short` to display available sinks
# run `sudo -u pulse alsamixer` to adjust volume levels
# run `sudo alsactl store` so save the volume levels persistently