Making a tracker controller survive reboots with systemd
Anatomy of the unit file
Restart policy without restart storms
Two services, one pattern
A snippet from the implementation
Straight from the example as deployed on the Raspberry PLC 21 — copy it freely:
[Unit]
Description=Solar tracker control (PyEphem + CANopen + TCP Node-RED)
# Starts once the network is up and the CAN bus is already configured
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
# Main tracker control script
ExecStart=/usr/bin/python3 /home/pi/tracker/main.py
WorkingDirectory=/home/pi/tracker
# Needs root for socketcan and GPIO
User=root
# Automatic restart if the process dies (CAN bus failure, exception, etc.)
Restart=always
RestartSec=10
# Logs to journald (check with journalctl -u solar-tracker.service)
StandardOutput=journal
StandardError=journal
# Avoid aggressive restart loops if something is truly broken
StartLimitIntervalSec=300
StartLimitBurst=10
[Install]
WantedBy=multi-user.targetThe full example is a complete program — wiring header, setup and main loop — ready to adapt to your application.
Frequently asked questions
Why run the service as root instead of a dedicated user?
The control process needs raw access to the CAN interface and GPIO character devices. You can tighten this with group permissions and udev rules, but on a single-purpose industrial PLC, root with a hardened image is the pragmatic baseline.
How does Authelia fit in front of Node-RED?
Authelia sits as an authentication portal in front of the dashboard, so every tab — control, calibration, inverter, configuration — requires login. Node-RED itself stays bound locally and never faces the network unauthenticated.
How do I check why the service failed in the field?
systemctl status solar-tracker.service shows the last state and exit code, and journalctl -u solar-tracker.service --since today gives the full log. Because StandardOutput goes to journald, no separate log files need rotating.