systemd User Services: Running Your Own Services Without Root
Most people meet systemd as the thing that runs system services as root. But every logged-in user also gets their own systemd instance, and you can use it to run your own services: a Syncthing daemon, a development server, an rclone mount, a backup timer. No root needed, and you get restarts, logging, and dependencies for free.
If systemd itself is new, start with what systemd is and writing service files.
System vs user instance
| System instance | User instance | |
|---|---|---|
| Command | systemctl (with sudo) | systemctl --user |
| Runs as | root, or a User= | You |
| Unit files | /etc/systemd/system/ | ~/.config/systemd/user/ |
| Logs | journalctl -u name | journalctl --user -u name |
| Starts | At boot | At first login (or boot, with lingering) |
| Boot target | multi-user.target | default.target |
Many desktop components already run this way: check with
systemctl --user list-units --type=service
and you will likely see PipeWire, WirePlumber, and desktop helpers.
Writing a user service
Create ~/.config/systemd/user/devserver.service:
[Unit]
Description=Local development server
After=network-online.target
[Service]
WorkingDirectory=%h/projects/site
ExecStart=/usr/bin/python3 -m http.server 8000 --bind 127.0.0.1
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
Notes:
%hexpands to your home directory; avoid hard-coding/home/youWantedBy=default.target, notmulti-user.target, which does not exist in user instances- No
User=line: it already runs as you
Then:
systemctl --user daemon-reload
systemctl --user enable --now devserver.service
systemctl --user status devserver.service
journalctl --user -u devserver.service -f
The systemd unit builder can generate a starting file.
Lingering: keep services running after logout
By default your user instance starts at your first login and stops when your last session ends. Log out of the desktop or close your SSH session, and your services stop.
Lingering changes that:
sudo loginctl enable-linger "$USER"
loginctl show-user "$USER" --property=Linger
With lingering on, your user instance starts at boot and keeps running whether or not you are logged in. This is what you want for a home server running services under a normal account, and it is how Syncthing and rootless Podman Quadlet containers are usually run.
User timers
Timers work exactly as in the system instance. ~/.config/systemd/user/backup.timer:
[Unit]
Description=Nightly backup of my documents
[Timer]
OnCalendar=*-*-* 02:30
Persistent=true
[Install]
WantedBy=timers.target
paired with a backup.service that runs your backup command. Enable the timer, not the service:
systemctl --user enable --now backup.timer
systemctl --user list-timers
Our systemd timers guide and systemd timer builder cover scheduling syntax. For your timer to fire when you are not logged in, enable lingering.
Pitfalls
systemctl —user fails over su or sudo
Failed to connect to bus: No medium found
systemctl --user talks to your user instance through XDG_RUNTIME_DIR (/run/user/UID) and a D-Bus socket. A real login (desktop, SSH, or console) sets these up. su - otheruser or sudo -u otheruser usually does not. Use:
sudo machinectl shell otheruser@
or log in as that user directly.
The environment is minimal
User services do not inherit your shell’s environment, aliases, or PATH additions from .bashrc. Use full paths in ExecStart, and set variables explicitly:
[Service]
Environment=NODE_ENV=production
EnvironmentFile=%h/.config/devserver.env
Graphical apps that need the display get WAYLAND_DISPLAY and DISPLAY imported automatically by most desktops at login.
Ports below 1024
A user service cannot bind ports below 1024. Use a high port and put a reverse proxy in front, or adjust net.ipv4.ip_unprivileged_port_start with sysctl if you understand the implications.
Resource limits and sandboxing
User services support many of the same directives as system services, such as MemoryMax= and CPUQuota=, through cgroups delegation. Some sandboxing directives that need privileges are unavailable or limited in user instances.
When to use a user service
Good fit: personal daemons (Syncthing, rclone mounts, sync tools), development servers, per-user timers, rootless containers, and anything you want supervised without giving it root.
Not a good fit: services that need root, low ports, or must be available to other users and the system before anyone logs in, unless you enable lingering and accept that it runs as your account.
Frequently Asked Questions
What is a systemd user service?
A service managed by a per-user instance of systemd rather than the system-wide one. It runs as your user, needs no root to install or control, and gets the same features as system services: automatic restarts, dependencies, logging to the journal, and timers.
Where do user unit files go?
Your own units go in ~/.config/systemd/user/. Units provided for all users by packages live in /usr/lib/systemd/user/, and administrators can add units for every user in /etc/systemd/user/.
Why does my user service stop when I log out?
By default the per-user systemd instance starts at your first login and stops after your last session ends, taking your services with it. Run loginctl enable-linger with your username so it starts at boot and keeps running whether or not you are logged in.
Why does systemctl —user fail over SSH or with su?
systemctl —user needs the XDG_RUNTIME_DIR and D-Bus session address of your user instance. A proper login, including SSH, sets these up. Switching users with su or sudo -u usually does not, so use machinectl shell user@ or log in directly as that user instead.
How do I see logs for a user service?
Use journalctl —user -u servicename. Add -f to follow new lines and -b for the current boot. If user journals are not persistent on your system, logs may only cover the current boot.
What should WantedBy be for a user service?
default.target. User instances do not have multi-user.target; default.target is the equivalent target that is reached when your user instance starts, so WantedBy=default.target makes the service start automatically.