We regularly livestream services at my church for those that are unable to attend in person. The entire system is fully automated, muxing an RTSP stream from an IP camera with audio from the main mixer. This all runs on a headless Linux machine via a cron job using FFmpeg, using the pulse filter to pull audio from the machine’s mic/line-in jack. It’s possible ALSA could be used directly here, however I decided to stick with Pulse.

By default, a user must be logged in order to record/play audio using PulseAudio. This is because PulseAudio does not run as a system-wide process but rather within a user session. It is possible to modify this behavior for use-cases such as mine where users aren’t normally logged in and the server itself is headless.

From the docs:

Starting with PulseAudio 0.9.3 the daemon can be run as a system-wide instance which than can be shared by multiple local users. We recommend running the PulseAudio daemon per-user, just like the traditional ESD sound daemon. In some situations however, such as embedded systems where no real notion of a user exists, it makes sense to use the system-wide mode.

The docs also do a good job of calling out the downsides/limitations of running the daemon system-wide so make sure this modification makes sense for your use-case. With that in mind, here are the steps I took to run the pulse server system-wide.

Note: I’m using systemd but if your *nix uses a different init system you should still be able to follow along.

1. Create a systemd service

Create a custom service at /etc/systemd/system/pulseaudio.service with the following content.

[Unit]
Description=Pulse Audio

[Service]
Type=notify
ExecStart=/usr/bin/pulseaudio  --daemonize=no --realtime --log-target=journal --disallow-module-loading --system --disallow-exit --disable-shm --exit-idle-time=-1
Restart=on-failure

[Install]
WantedBy=default.target

2. Edit the PulseAudio client config

Next we need to disable auto-spawning and set the default server.

Edit the config file at /etc/pulse/client.conf to look like the following:

# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PulseAudio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.

## Configuration file for PulseAudio clients. See pulse-client.conf(5) for
## more information. Default values are commented out.  Use either ; or # for
## commenting.

; default-sink =
; default-source =
; default-server = /var/run/pulse/native
; default-dbus-server =

; autospawn = no
; daemon-binary = /usr/bin/pulseaudio
; extra-arguments = --log-target=syslog

; cookie-file =

; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB

; auto-connect-localhost = no
; auto-connect-display = no

3. Add the user to the corresponding groups

For the user that the service will be running under, add them to the pulse-access and audio groups.

sudo usermod -a -G audio pulse
sudo usermod -a -G pulse-access <USER>

If the user you’re running under doesn’t have access to the audio group you’ll need to add them to that group as well.

4. Enable the systemd service

Run the following command to start the systemd unit when a user logs in.

systemctl --user enable pulseaudio

Verify the service started successfully with the following command

systemctl --user status pulseaudio

Also verify that you can see your audio devices. If you see null sinks and sources that means things aren’t working correctly.

pactl list short

5. Enable linger

Since we want to be able to access our devices without being logged in via SSH for example, we need to set our user to linger. This creates a user session that persists even across restarts.

sudo loginctl enable-linger <USER>

Reboot your system and verify your script functions properly without you being logged in.

6. Adjust audio levels

Finally, adjust your audio levels by running alsamixer

Tada 🎉. You can now record audio without being logged in!