By default a system service runs as root. Almost nothing should. Run it as a least-privileged user instead.
With an existing user
[Service]
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/server
Restart=on-failure
Create the dedicated, login-less system user first if it doesn’t exist:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp
WorkingDirectory=is thecwdof the process (relative paths in your program resolve from here).- The user needs read/execute on the binary and read/write only on the data dirs it actually uses.
Or let systemd invent a user (DynamicUser)
[Service]
DynamicUser=yes
StateDirectory=myapp
ExecStart=/opt/myapp/bin/server
DynamicUser=yes runs the service as a transient UID that exists only while it runs — no useradd needed. Pair it with StateDirectory=myapp, which auto-creates /var/lib/myapp owned by that transient user (also CacheDirectory=, LogsDirectory=, RuntimeDirectory=).
Apply and confirm
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
systemctl show myapp.service -p MainPID
ps -o user= -p "$(systemctl show -p MainPID --value myapp.service)"
Gotchas: if the service binds a port below 1024 as a non-root user, grant just that capability with AmbientCapabilities=CAP_NET_BIND_SERVICE instead of running as root. With DynamicUser=yes, the user is different on every start, so never write files to a fixed path you own — use the *Directory= options, which set ownership for you.