Loading...
Loading...
Create and harden systemd service unit files following modern best practices. Use when writing new systemd units for web applications, background workers, or daemons, or when hardening existing services with security sandboxing and isolation features. Covers service types, dependencies, restart policies, security options, and filesystem restrictions.
npx skill4agent add rcgsheffield/skills systemd-unitsUser request involves systemd service?
│
├─ Creating a new service?
│ │
│ ├─ Web application/API → Use "Creating a New Service" workflow
│ │ → Start from assets/basic-webapp.service or assets/hardened-webapp.service
│ │
│ ├─ Background worker/daemon → Use "Creating a New Service" workflow
│ │ → Start from assets/background-worker.service
│ │
│ └─ One-time initialization → Use "Creating a New Service" workflow
│ → Start from assets/oneshot-init.service
│
└─ Hardening existing service?
│
└─ Use "Hardening an Existing Service" workflow
→ Reference references/systemd_options.md for security options
→ Compare against assets/hardened-webapp.service for patternsassets/basic-webapp.servicehardened-webapp.servicebackground-worker.serviceoneshot-init.service[Unit]
Description=Clear description of what this service does
Documentation=https://example.com/docs or man:program(8)
After=network-online.target database.service
Wants=network-online.target
Requires=database.serviceAfter=network-online.targetWants=network-online.targetType=notifyType=execType=oneshotType=simpleType=forking[Service]
Type=exec
ExecStart=/usr/bin/node /opt/webapp/server.js
WorkingDirectory=/opt/webappExecStart=# Option 1: Dynamic user (recommended for security)
DynamicUser=yes
# Option 2: Specific user/group
User=webapp
Group=webapp
# Environment configuration
Environment="NODE_ENV=production"
EnvironmentFile=-/etc/webapp/webapp.envDynamicUser=yesEnvironmentFile=Restart=on-failure # For web apps (restart on crashes)
RestartSec=10 # Wait 10 seconds before restart
TimeoutStartSec=30 # Fail if startup takes >30 seconds
TimeoutStopSec=30 # Force kill if graceful stop takes >30 secondsRestart=on-failureRestartSec=10Restart=alwaysRestartSec=5Restart=# Filesystem protection
ProtectSystem=strict # Entire filesystem read-only except specified paths
ProtectHome=yes # Hide /home directories
PrivateTmp=yes # Private /tmp namespace
ReadWritePaths=/var/lib/myapp /var/log/myapp # Writable paths whitelist
NoNewPrivileges=yes # Prevent privilege escalation
# Device and kernel protection
PrivateDevices=yes # Restrict device access
ProtectKernelTunables=yes # Prevent /proc/sys, /sys writes
ProtectKernelModules=yes # Prevent kernel module loading
ProtectControlGroups=yes # Protect cgroup filesystem# Network restrictions
RestrictAddressFamilies=AF_INET AF_INET6 # Only IPv4/IPv6
# Capability restrictions
CapabilityBoundingSet= # Remove all capabilities
# System call filtering
SystemCallFilter=@system-service
SystemCallArchitectures=native
# Memory protection
MemoryDenyWriteExecute=yes # W^X protection
LockPersonality=yesjournalctl -xeu <service>systemd-analyze security <service>[Install]
WantedBy=multi-user.target # Most common target (non-graphical multi-user system)multi-user.targetgraphical.targetdefault.target# Copy to system directory
sudo cp myapp.service /etc/systemd/system/
# Reload systemd to recognize new service
sudo systemctl daemon-reload
# Start and check status
sudo systemctl start myapp
sudo systemctl status myapp
# View logs
journalctl -xeu myapp
# Enable to start on boot
sudo systemctl enable myapp# Check current security exposure
systemd-analyze security <service-name>
# Review current configuration
systemctl cat <service-name>sudo systemctl edit <service-name>/etc/systemd/system/<service-name>.service.d/override.conf[Service]
# Filesystem protection
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/myapp # Add paths service needs to write
# Basic device protection
PrivateDevices=yessudo systemctl restart <service>journalctl -xeu <service>[Service]
# Kernel protection
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
# Remove capabilities
NoNewPrivileges=yes
CapabilityBoundingSet=[Service]
# Network restrictions (adjust for service needs)
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
# System call filtering
SystemCallFilter=@system-service
SystemCallArchitectures=native
# Memory protection
MemoryDenyWriteExecute=yes
LockPersonality=yesReadWritePaths=ProtectSystem=fullstrictRestrictAddressFamilies=AF_UNIXjournalctlSystemCallFilter=@system-service <syscall-name>DeviceAllow=PrivateDevices=# Check security score (lower is better)
systemd-analyze security <service-name>
# Verify service functionality
sudo systemctl restart <service-name>
sudo systemctl status <service-name>
# Test application-level functionality
# (Make requests, check logs, verify operations)Type=notifyType=execType=simpleType=forkingType=notifyType=execAfter=Requires=Wants=After=Requires=Wants=Wants=Requires=After=network-online.targetWants=network-online.targetDynamicUser=yesEnvironmentFile=NoNewPrivileges=yesCapabilityBoundingSet=systemd-analyze securityProtectSystem=strictReadWritePaths=PrivateTmp=yesProtectHome=yes/var/lib/Restart=on-failureRestartSec=10Restart=alwaysRestartSec=5TimeoutStartSec=30TimeoutStopSec=30SuccessExitStatus=StandardOutput=journalStandardError=journalSyslogIdentifier=journalctl -xeu <service>systemctl list-dependencies <service>grep -i "ProtectSystem" references/systemd_options.mdbasic-webapp.servicehardened-webapp.servicebackground-worker.serviceoneshot-init.servicejournalctl -xeu <service>WorkingDirectory=EnvironmentFile=ReadWritePaths=journalctl -xeu <service>ExecStart=RestartSec=Type=RestrictAddressFamilies=AF_INETAF_INET6AF_UNIXAF_INET