User services let you run systemd units as a regular user, without needing root privileges.
Let’s see git-daemon in action. Normally, you’d run this as root, but we want it to run as git user.
# Create a dummy git user if it doesn't exist
sudo useradd -r -s /bin/false git
sudo mkdir -p /home/git
sudo chown git:git /home/git
# Create a directory for git repos
sudo mkdir -p /srv/git
sudo chown git:git /srv/git
# Create a dummy git repository
sudo -u git git init --bare /srv/git/myrepo.git
sudo -u git git --git-dir=/srv/git/myrepo.git config http.receivepack true
# Create the systemd user service file
mkdir -p ~/.config/systemd/user/
cat <<EOF > ~/.config/systemd/user/git-daemon.service
[Unit]
Description=Git Daemon for user services
After=network.target
[Service]
ExecStart=/usr/bin/git daemon --user=git --group=git --reuseaddr --base-path=/srv/git /srv/git
Restart=on-failure
[Install]
WantedBy=default.target
EOF
# Enable and start the service
systemctl --user enable git-daemon.service
systemctl --user start git-daemon.service
# Check status
systemctl --user status git-daemon.service
Now, you can clone this repository from another machine:
git clone git://localhost/myrepo.git
This works because systemd, when invoked with --user, spawns a dedicated systemd instance for that user, isolated from the root systemd. This user instance manages its own set of units, processes, and resources, all under the user’s UID. It’s like a mini-systemd running just for you.
The [Install] section with WantedBy=default.target is key. default.target is the standard target that systemd brings up for a user session. When you log in, your user systemd instance starts, and it looks for units that want to be part of default.target and starts them.
The ExecStart line is where the magic happens. We’re explicitly telling git daemon to run as the git user and group, and to serve from /srv/git. systemd --user respects these directives, ensuring the process is launched with the correct user context.
You can also manage these services with systemctl --user. systemctl --user status, systemctl --user start, systemctl --user stop, and systemctl --user enable all operate on the user’s systemd instance, not the system-wide one.
The crucial part of running services as a non-root user within systemd is understanding the User= and Group= directives in the [Service] section of your unit file. However, when using systemd --user, these directives are often redundant if the service itself is designed to be run by a specific user (like git daemon --user=git). The user systemd instance is already running as that user. The primary benefit here is not necessarily changing the user a service runs as (though you can), but running any service, managed by systemd, without root.
The dbus-launch process is automatically managed by the user systemd, and it’s essential for many user-level applications that need to communicate via D-Bus. If you see errors related to D-Bus, it’s often because the user’s systemd hasn’t properly started this component.
The next step is to explore how user services interact with systemd’s socket activation.