#
Managing Applications with systemd User Services
Sometimes, your application needs to be managed like a system service. Easy to start, stop, restart, and ideally run automatically on system boot or after crashes. This is where systemd user services come in handy.
You may already be familiar with tools like supervisord or PM2, but systemd
is a powerful native alternative that integrates well with Linux systems, offers better logging, and doesn’t require root access when used as a user service.
This guide will walk you through the basics of setting up, troubleshooting, and applying systemd
user services for various applications, including CMSes and frameworks.
#
How to Set Up a systemd User Service
Setting up a systemd
user service allows your app to automatically start on login, recover after failure, or be controlled like any other service.
#
Quick Setup
Create the service file under:
~/.config/systemd/user/your-app.service
Example:
[Unit]
Description=My App Service
After=network.target
StartLimitIntervalSec=0
[Service]
ExecStart=/var/www/username/path-to-your-app/start.sh
RestartSec=10s
Restart=always
Environment=NODE_ENV=production
WorkingDirectory=/var/www/username/path-to-your-app/
[Install]
WantedBy=default.target
Hint:
StartLimitIntervalSec=0
allows endless retrying every 10 seconds. Without it, systemd may stop trying after repeated failures.
Enable and start the service:
systemctl --user daemon-reload
systemctl --user enable your-app.service
systemctl --user start your-app.service
Enable lingering (start at boot even without an active login):
loginctl enable-linger your-username
For a deeper look: Arch Wiki - Systemd/User
#
How to Troubleshoot a systemd User Service
systemd
keeps logs that help determine why a service might have crashed. Use journalctl
for this purpose.
#
Show the status of the service:
systemctl --user status your-app.service
#
View logs from the last 5 minutes:
journalctl --user -u your-app.service --since "5 minutes ago"
#
Show the last 1000 log lines:
journalctl --user --no-pager -n 1000
#
Search for fatal errors in a service log:
journalctl --user -u shopware_consumer* --no-pager -n 1000 | grep -i FATAL
#
Live log feed:
journalctl --user -n 100 -f
#
Examples and CMS/Framework Specific Notes
#
Akeneo
Path: ~/.config/systemd/user/pim_job_queue@.service
[Unit]
Description=Akeneo PIM Job Queue Service (#%i)
After=network-online.target
Requires=dbus.socket
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=%h/akeneo
ExecStart=%h/akeneo/bin/console messenger:consume --env=prod %I
RestartSec=10s
Restart=always
[Install]
WantedBy=default.target
Note: Adjust the
WorkingDirectory
to match your setup.
#
Laravel
Path: ~/.config/systemd/user/laravel-queue@.service
[Unit]
Description=Laravel queue worker (#%i)
After=network-online.target
Requires=dbus.socket
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=%h/app
ExecStart=/usr/local/php74/bin/php artisan queue:work --daemon --env=prod
RestartSec=10s
Restart=always
[Install]
WantedBy=default.target
Note: Adjust the PHP binary path and the
WorkingDirectory
to match your setup.
#
CraftCMS
CraftCMS includes a queue system that handles background tasks such as image processing, email sending, and plugin actions. By default, these are run within web requests, which can cause performance issues.
A better approach is to run the queue listener as a dedicated systemd
service.
Path: ~/.config/systemd/user/craftcms-queue@.service
[Unit]
Description=CraftCMS queue runner (#%i)
After=network-online.target
Requires=dbus.socket
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=%h/domains/example.com/craftcms
ExecStart=/usr/local/php80/bin/php %h/domains/example.com/craftcms/craft queue/listen
ExecReload=/bin/kill -SIGABRT $MAINPID
RestartSec=10s
Restart=always
[Install]
WantedBy=default.target
Note:
- Disable
runqueueautomatically
in the general config settings.- Adjust PHP binary path and
WorkingDirectory
as needed.
🔗 Extra Info:
#
Magento
Magento uses message queues for async tasks. You can manage its consumers with systemd
.
Path: ~/.config/systemd/user/magento-consumer@.service
[Unit]
Description=Magento Consumer (%i)
After=network-online.target
Requires=dbus.socket
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=%h/public_html
ExecStart=php %h/public_html/bin/magento queue:consumers:start %I --single-thread --max-messages=10000
RestartSec=10s
Restart=always
[Install]
WantedBy=default.target
#
General Information
%i
is a placeholder for the consumer name—reusable template%h
resolves to the user’s home directory--single-thread
ensures one thread per process--max-messages=10000
helps restart periodically to prevent memory issues
Examples:
systemctl --user status magento-consumer@sales.rule.update.coupon.usage.service
systemctl --user status magento-consumer@product_action_attribute.update.service
You can repeat this for any other queue. Each one runs in its own isolated service.