Running Rocrail as a systemd service

A systemd unit that starts the Rocrail server at boot, restarts it if it dies, and runs it as an unprivileged user.

Rocrail launched from a terminal dies with the terminal and does not survive a reboot. On a machine whose whole job is controlling a layout, it should start itself and stay started.

The unit file

Create /etc/systemd/system/rocrail.service:

[Unit]
Description=Rocrail model railway control server
Documentation=https://wiki.rocrail.net/
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=rocrail
Group=rocrail
WorkingDirectory=/opt/rocrail
ExecStart=/opt/rocrail/rocrail -l /opt/rocrail -w /opt/rocrail
Restart=on-failure
RestartSec=10

# Rocrail needs nothing beyond its own directory, so give it nothing else.
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/opt/rocrail

[Install]
WantedBy=multi-user.target

Adjust WorkingDirectory, ExecStart and ReadWritePaths to wherever your installation actually lives.

Why these settings

After=network-online.target rather than plain network.target. Rocrail opens a TCP connection to the command station at startup; starting before the network is genuinely up means that connection fails and Rocrail sits there unhappy. network-online.target waits for an actual usable network.

Restart=on-failure with RestartSec=10. If Rocrail exits with an error it comes back ten seconds later. The delay matters — without it, a service that fails instantly gets restarted in a tight loop and systemd eventually gives up and leaves it stopped.

A dedicated rocrail user. There is no reason for this to run as root. It needs its own directory and a network socket, nothing more.

The Protect* lines. Standard systemd hardening: no access to home directories, read-only system directories, private /tmp. ReadWritePaths then punches through ProtectSystem=full for the one directory Rocrail genuinely needs to write to.

Setting it up

Create the user and hand over the directory:

sudo useradd --system --home-dir /opt/rocrail --shell /usr/sbin/nologin rocrail
sudo chown -R rocrail:rocrail /opt/rocrail

Then enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now rocrail

Checking it works

systemctl status rocrail

You want active (running) and enabled. Enabled is the half people forget — a service can be running perfectly and still not come back after a reboot.

Follow the log as it starts:

journalctl -u rocrail -f

You are looking for the command station connecting and reporting its firmware version. If it does not, the problem is the controller settings rather than systemd — see Rocrail and DCC-EX.

Test the reboot

Actually reboot the machine and confirm Rocrail comes back on its own. A unit file that looks correct and a unit file that works are not the same thing, and the gap between them is usually a missing enable or a permissions problem on the working directory.

If it will not start

  • status=203/EXEC — the path in ExecStart is wrong, or the binary is not executable.
  • Permission denied in the log — the rocrail user cannot write to its working directory. Check the chown, and check ReadWritePaths covers it.
  • Starts then immediately exits — run the same ExecStart command by hand as the rocrail user and read what it prints. systemd will not show you a message that Rocrail wrote to a terminal before exiting.

Where to go next

More on rocrail integration