User and Group Isolation in systemd
Configure User=, Group=, DynamicUser=, and SupplementaryGroups= in systemd services. Learn how to create dedicated service users and isolate services from each other.
Detailed Explanation
Running Services as Non-Root Users
Running services as root is a security risk. Systemd makes it easy to run services as dedicated users with minimal privileges.
Basic User/Group Configuration
[Service]
User=myapp
Group=myapp
The user and group must exist on the system. Create them before enabling the service:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp
The flags ensure:
--system: Creates a system user (UID below 1000)--no-create-home: No home directory--shell /usr/sbin/nologin: Cannot log in interactively
DynamicUser — Ephemeral Users
DynamicUser=yes creates a temporary user that exists only while the service runs:
[Service]
DynamicUser=yes
ExecStart=/usr/local/bin/myapp
StateDirectory=myapp
CacheDirectory=myapp
LogsDirectory=myapp
Benefits:
- No need to create users manually during installation
- User IDs are allocated from a dedicated range (61184-65519)
- Automatic cleanup when the service stops
- Forced
ProtectSystem=strictandProtectHome=read-only
Pair with StateDirectory=, CacheDirectory=, and LogsDirectory= because DynamicUser restricts filesystem access.
SupplementaryGroups
Grant access to additional resources via supplementary groups:
[Service]
User=myapp
Group=myapp
SupplementaryGroups=ssl-cert docker
Common supplementary groups:
ssl-cert: Access to SSL certificates in/etc/ssl/private/docker: Access to the Docker socketadm: Access to log files in/var/log/audio,video: Access to hardware devices
File Ownership Preparation
Ensure the service user owns its data directories:
sudo mkdir -p /opt/myapp/data
sudo chown -R myapp:myapp /opt/myapp/data
Or let systemd manage it with StateDirectory=:
[Service]
StateDirectory=myapp
# Creates /var/lib/myapp/ owned by the service user
Systemd-Managed Directories
| Directive | Creates | Typical Use |
|---|---|---|
StateDirectory= |
/var/lib/{name}/ |
Persistent data |
CacheDirectory= |
/var/cache/{name}/ |
Cached data |
LogsDirectory= |
/var/log/{name}/ |
Log files |
RuntimeDirectory= |
/run/{name}/ |
Sockets, PID files |
ConfigurationDirectory= |
/etc/{name}/ |
Configuration |
These directories are created with correct ownership before ExecStart runs and are the recommended approach for managing service data.
Combining with Hardening
[Service]
User=myapp
Group=myapp
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/opt/myapp/data
PrivateTmp=yes
This creates a service that runs as a dedicated user with minimal filesystem access and no ability to escalate privileges.
Use Case
Setting up proper process isolation for multiple services on the same server, ensuring each service runs as a dedicated user with access only to its own data directories and required system resources.