Managing services for non-root users with systemd
Systemd is mainly used to manage services on modern linux distributions, but it also allows non-root users to manage services running under their own account. This makes systemd a great alternative to services like supervisord as it is able to detect crashes and automatically restarts the service.
Configuring a user unit
The directory structure that holds unit files is similar to the systemwide configuration, but is located in a user’s home directory:
$ mkdir -p ~/.config/systemd/user
In the user folder we can create service units with a .service extension. A unit is a ini-style file describing a service that has to be managed.
Let’s say we have a Laravel PHP queue worker that we want to be managed by systemd, called worker.service :
[Unit] Description=Laravel PHP queue worker [Service] ExecStart=/usr/bin/env php /path/to/my/project/artisan queue:work --sleep=3 --tries=3 [Install] WantedBy=default.target
Run $ systemctl —user daemon-reload to make systemd recognize your changes.
We can now manage this service with systemd:
- $ systemctl —user start worker starts the service
- $ systemctl —user stop worker stops the service
- $ systemctl —user restart worker restarts the workers
- $ systemctl —user status worker shows us the service status
If we want this service to start automatically when our user logs in, we have to enable it:
Viewing logs
Everything our PHP script writes to stdout and stderr will be recorded by journald. We can view the logs for this specific service:
On older versions of systemd (like the one shipped with Debian Jessie), you might need to use a flag specific to user units:
$ journalctl --user-unit worker
Automatically restarting on failure
Let’s be honest: things go wrong. When our worker crashes we want to make sure it’s started again. Fortunately, we can configure systemd to restart on failure:
Add the following to the [Service] section of the unit file:
RestartSec=10s Restart=on-failure
This makes systemd restart the service 10 seconds after it has stopped with a non-zero exit code. If the process exited cleanly, systemd considers the work as done and won’t restart the service until you manually start it later.
Starting user services at boot
By default, systemd starts enabled user units when the user logs in, and stops them once the user has logged out. This is great for desktop pcs where a user wants to run things while he’s using the computer. On servers, however, we may want to run those units all the time.
# loginctl enable-linger USERNAME
You can check the status for each user by:
# loginctl user-status USERNAME
It should show that the user is lingering:
vic (1000) Since: Fri 2017-09-01 21:54:29 CEST; 3s ago State: lingering Unit: user-1000.slice
When the user is not logged in and does not have lingering enabled, loginctl doesn’t know about any user sessions so it will return an error:
Failed to get user: No user 1000 known or logged in