Problems starting mavproxy as a systemd service

What happens

I installed mavproxy on my companion computer (RPi 4) and now try to register it as a systemd service.
I’ve installed everything in a virtual environment at /home/pi/venv_mission. A mavproxy startup script is installed at /home/pi/.mavinit.scr and starts the NTRIP module. Also, I’ve created a runner script at /home/pi/mavproxy_runner.sh with the following contents:

#!/bin/bash
source /home/pi/venv_mission/bin/activate
mavproxy.py --master /dev/ttyACM0 --out 127.0.0.1:14550 --moddebug=3

I’ve furthermore created a systemd script /etc/systemd/system/mavproxy.service as follows:

[Unit]
Description=Mavproxy with NTRIP for RTK drones
After=network.target

[Service]
WorkingDirectory=/home/pi
ExecStart=/home/pi/mission/mavproxy_runner.sh
Restart=always
User=pi

[Install]
WantedBy=multi-user.target

Running the service with sudo systemctl start mavproxy, it fails after a few seconds with sudo systemctl status mavproxy returning the following:

mavproxy.service - Mavproxy with NTRIP for RTK drones
Loaded: loaded (/etc/systemd/system/mavproxy.service; disabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Mon 2023-11-13 10:47:00 CET; 4s ago
Process: 2272 ExecStart=/home/pi/mission/mavproxy_runner.sh (code=exited, status=1/FAILURE)
Main PID: 2272 (code=exited, status=1/FAILURE)

Nov 13 10:47:00 drone-pi systemd[1]: mavproxy.service: Service RestartSec=100ms expired, scheduling restart.
Nov 13 10:47:00 drone-pi systemd[1]: mavproxy.service: Scheduled restart job, restart counter is at 5.
Nov 13 10:47:00 drone-pi systemd[1]: Stopped Mavproxy with NTRIP for RTK drones.
Nov 13 10:47:00 drone-pi systemd[1]: mavproxy.service: Start request repeated too quickly.
Nov 13 10:47:00 drone-pi systemd[1]: mavproxy.service: Failed with result ‘exit-code’.
Nov 13 10:47:00 drone-pi systemd[1]: Failed to start Mavproxy with NTRIP for RTK drones.

There isn’t any more useful info on the systemd side. When I attach >>home/pi/log.log to the mavproxy command in the runner script, I get the following from mavproxy:

Connect /dev/ttyACM0 source_system=255
pymavlink too old; using old rallypoint_protocol module
Loaded module rallypoint_protocol
pymavlink too old; using old fenceitem_protocol module
Loaded module fenceitem_protocol
Running script (/home/pi/.mavinit.scr)
Running script /home/pi/.mavinit.scr
→ module load ntrip
Loaded module ntrip
→ ntrip set caster ntrip.com
→ ntrip set port 2101
→ ntrip set mountpoint foo
→ ntrip set username foo@bar.de
→ ntrip set password none
→ ntrip start
Start delayed pending position
Log Directory:
Telemetry log: mav.tlog
Waiting for heartbeat from /dev/ttyACM0
MAV> Unloading module link
Unloading module log
Unloading module signing
Unloading module wp
Unloading module rally
Unloading module rally
Unloading module fence
Unloading module fence
Unloading module ftp
Unloading module param
Unloading module relay
Unloading module tuneopt
Unloading module arm
Unloading module mode
Unloading module calibration
Unloading module rc
Unloading module auxopt
Unloading module misc
Unloading module cmdlong
Unloading module battery
Unloading module terrain
Unloading module output
Unloading module adsb
Unloading module layout
Unloading module ntrip

This output repeats 5 times, problably as systemd retries a failed start four times.

What I expect

When I run the runner scrip manually, it runs flawless:

pi@drone-pi:~ $ ./mission/mavproxy_runner.sh
Connect /dev/ttyACM0 source_system=255
pymavlink too old; using old rallypoint_protocol module
Loaded module rallypoint_protocol
pymavlink too old; using old fenceitem_protocol module
Loaded module fenceitem_protocol
Running script (/home/pi/.mavinit.scr)
Running script /home/pi/.mavinit.scr
→ module load ntrip
Loaded module ntrip
→ ntrip set caster ntrip.com
→ ntrip set port 2101
→ ntrip set mountpoint foo
→ ntrip set username foo@bar.de
→ ntrip set password none
→ ntrip start
Start delayed pending position
Log Directory:
Telemetry log: mav.tlog
Waiting for heartbeat from /dev/ttyACM0
MAV> Detected vehicle 1:1 on link 0
online system 1
ALT_HOLD> Mode ALT_HOLD
fence present
AP: ArduCopter V4.3.7 (c8506ed4)
AP: ChibiOS: a97ccb41
AP: CubeOrangePlus 003F0039 30325117 37363931
AP: RCOut: PWM:1-8 DS150:9-12 PWM:13-14
AP: IMU0: fast sampling enabled 2.0kHz
AP: IMU1: fast sampling enabled 9.0kHz/2.3kHz
AP: IMU2: fast sampling enabled 9.0kHz/2.3kHz
AP: Frame: QUAD/X
Received 1008 parameters (ftp)
Saved 1008 parameters to mav.parm
Flight battery 100 percent
NTRIP started
GPS lock at 537 meters

If I try to simulate the systemd startup by running

sudo runuser -c "/home/pi/mission/mavproxy_runner.sh"

, it also runs without problems (though it doesn’t find the startup script, but that’s a problem of the future).

I think, I just solved it.
Mavproxy can’t start an interactive shell when running in the background. The attempt can be suppressed with the --daemon parameter. Thus, my current systemd script becomes

[Unit]
Description=Mavproxy with NTRIP for RTK drones
RequiredBy=mission.service

[Service]
WorkingDirectory=/home/pi
ExecStart=/home/pi/venv_mission/bin/mavproxy.py --master /dev/ttyACM0 --out 127.0.0.1:14550 --daemon
Restart=always
User=pi

[Install]
WantedBy=multi-user.target

Upon closer inspection, the working examples on the internet all seem to have the daemon parameter, I simply overlooked it. Unfortunately, only few of them mention it’s significance.