I'm writing some code to run a sound/light show from a raspberry pi 3 A+, using the latest Bullseye OS. I've been testing it manually, running via ssh, and all is well. However, it needs to run headless and unattended, so now I'm trying to turn it into a systemd service, and can't get it to start properly on reboot.
At first, I tried it as a system service. But because it uses pulseaudio, which runs in userland, it was failing to connect to pulse - even with the User= and Group= options set. After much googling, it seems that common wisdom is to not run pulse system-wide, and instead run the script as a user service. That's fine by me, as it makes the runtime environment more like my testing environment. (Raspi-config is set to auto-login my user.)
I installed it in .config/systemd/user, with the following unit file, and enabled it with systemctl --user enable lpr
[Unit]
Description=LPR Service
After=network-online.target
[Service]
Type=idle
ExecStart=/home/meeotch/test/mqlighting.py
WorkingDirectory=/home/meeotch/test
StandardInput=null
StandardOutput=file:/home/meeotch/logs/mqlighting_log.txt
StandardError=file:/home/meeotch/logs/mqlighting_log.txt
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
For testing, I stripped down the actual script to a minimal:
#! /usr/bin/env python
import sys, os
import csv
import subprocess
print("FOO", flush=True)
audio_dir = "/home/meeotch/audio"
silence = os.path.join(audio_dir, "750-milliseconds-of-silence.mp3")
while 1:
pass
But when I reboot the pi, give it some time, then log in via ssh, I see the log file doesn't exist. And systemctl --user status lpr gives:
● lpr.service - LPR Service
Loaded: loaded (/home/meeotch/.config/systemd/user/lpr.service; enabled; vendor preset: enabled)
Active: inactive (dead)
grep -i lpr syslog
shows nothing interesting. It's just dead, and never wrote anything to the log file. But systemctl --user start lpr
works fine if I start it manually - the log file gets written, status shows as "running".
I've also tried with After=multi-user.target, but no love.
Any suggestions as to how I can figure out why it's dead, and not restarting on boot?
UPDATE: well, my several hours of googling seem to have not turned up a basic fact. From this thread: https://bbs.archlinux.org/viewtopic.php?id=250813 I discovered that neither multi-user.target nor network-online.target are user targets.
Swapped out the After= and WantedBy= to "default.target", and it seems to be working. However, it failed for another startup script that also uses networking (mqtt), with "OSError: [Errno 101] Network is unreachable".
So I guess now I have a follow-up question: the systemd docs say, "When a systemd user instance starts, it brings up the per user target default.target." It seems that this doesn't guarantee network services.
So what's the appropriate "late" target to use, when the service in question needs network, dbus, and other stuff? Ultimately, I'd like to turn off the GUI altogether, so graphical-session.target doesn't seem like a good choice.
UPDATE #2: So I discovered how to get a graphical representation with systemd-analyze plot > boot_analysis.svg
, which was pretty helpful. The last few targets to run are: bluetooth, multi-user, and graphical. Since multi-user isn't available to a user service, and graphical doesn't run when I switch the boot to "Console with Login" (to disable the window manager and stuff I don't need), I chose bluetooth.target for the second service that was failing with "Network is unreachable". And now that service seems to work, too.
One weird thing about systemd-analyze plot
is that it doesn't seem to show either of my services. I guess maybe it only shows system services, but also user-managed targets?
systemd
, but I can't help but wonder if you could get this accomplished sooner/easier with a simplecron
job?