Linux Services & Daemons Management Guide
Published: December 2025 | Topic: Linux Services & Daemons for DevOps
Services are the backbone of Linux systems. Understanding how to manage services with systemd is crucial for deploying, maintaining, and troubleshooting applications. This guide covers systemd, unit files, service lifecycle, and practical management techniques.
1. Understanding Systemd & Services
What is Systemd?
Systemd is a system and service manager for Linux operating systems. It's the first process to start (PID 1) and manages the boot process, service lifecycle, logging, and more.
Key Features: Parallel startup, on-demand activation, service dependencies, snapshot support, and comprehensive logging via journald.
Service Lifecycle States
Systemctl Basics
Service Status & Information
$ systemctl status nginx.service
$ systemctl status docker
$ systemctl is-active nginx # Returns: active/inactive
$ systemctl is-enabled nginx # Returns: enabled/disabled
$ systemctl is-failed nginx # Returns: failed/active
# List all units:
$ systemctl list-units
$ systemctl list-units --type=service
$ systemctl list-units --state=failed
$ systemctl list-unit-files # All installed unit files
Service Lifecycle Management
$ sudo systemctl start nginx
$ sudo systemctl stop nginx
$ sudo systemctl restart nginx
$ sudo systemctl reload nginx # Reload config without restart
$ sudo systemctl reload-or-restart nginx
# Enable/Disable at boot:
$ sudo systemctl enable nginx
$ sudo systemctl disable nginx
$ sudo systemctl enable --now nginx # Enable and start
# Mask/Unmask services:
$ sudo systemctl mask nginx # Prevent all starts
$ sudo systemctl unmask nginx # Remove mask
Service Types & Categories
Common Service Types
nginx.service
apache2.service # Debian/Ubuntu
httpd.service # RHEL/CentOS
# Database Servers:
mysql.service
postgresql.service
mongod.service
# Containerization:
docker.service
containerd.service
podman.service
# Monitoring & Logging:
prometheus.service
grafana-server.service
elasticsearch.service
fluentd.service
# System Services:
sshd.service # SSH Daemon
crond.service # Cron Daemon
systemd-journald.service # Journal Daemon
Finding Services
$ systemctl list-unit-files | grep nginx
$ systemctl list-units --all | grep mysql
# Find service by process:
$ systemctl status $(pidof nginx)
$ systemctl status $(pgrep mysqld)
# Check service dependencies:
$ systemctl list-dependencies nginx.service
$ systemctl list-dependencies --reverse nginx.service
# Show service properties:
$ systemctl show nginx.service
$ systemctl show --property=Requires nginx.service
$ systemctl show --property=After nginx.service
2. Understanding Unit Files
Unit File Structure
/lib/systemd/system/ # System-provided units
/etc/systemd/system/ # User/Admin created units
/usr/lib/systemd/system/ # Package-installed units (RHEL)
# Basic unit file structure:
[Unit]
Description=Service Description
Documentation=man:service(8) https://example.com
After=network.target
Requires=network.target
Wants=some-other.service
Conflicts=conflicting.service
[Service]
Type=simple
ExecStart=/usr/bin/command --options
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10
User=service-user
Group=service-group
Environment="KEY=value"
WorkingDirectory=/path/to/workdir
[Install]
WantedBy=multi-user.target
Alias=service-alias.service
Service Section Types
Type=simple (Default)
# Systemd considers service started immediately
# Good for long-running services
[Service]
Type=simple
ExecStart=/usr/bin/myserver
User=appuser
Restart=always
Type=forking
# Systemd needs PIDFile to track
# Traditional daemon behavior
[Service]
Type=forking
PIDFile=/var/run/myserver.pid
ExecStart=/usr/bin/myserver --daemon
ExecStop=/bin/kill $MAINPID
User=appuser
Type=oneshot
# Used for scripts/tasks
# Often combined with RemainAfterExit
[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Type=notify
# Uses sd_notify() system call
# For services that need to signal readiness
[Service]
Type=notify
ExecStart=/usr/bin/myserver --notify
NotifyAccess=all
TimeoutStartSec=30
Restart=on-failure
Key Directives Explained
[Unit] Section
Documentation=man:page or URL
After=service1.service service2.target
Requires=service1.service # Hard dependency
Wants=service2.service # Soft dependency
Conflicts=conflict.service # Cannot run together
Before=service3.service # Ordering
ConditionPathExists=/path/file
ConditionPathIsDirectory=/path
AssertPathExists=/path/file
[Service] Section
ExecStart=/path/to/command args
ExecStartPre=/path/to/pre-script
ExecStartPost=/path/to/post-script
ExecStop=/path/to/stop-command
ExecReload=/bin/kill -HUP $MAINPID
Restart=no|on-success|on-failure|always
RestartSec=10 # seconds before restart
User=username
Group=groupname
WorkingDirectory=/path
Environment="KEY=value"
EnvironmentFile=/etc/default/service
PIDFile=/var/run/service.pid
TimeoutStartSec=30
TimeoutStopSec=30
[Install] Section
RequiredBy=some.target
Also=other-unit.service # Install additional units
Alias=service-alias.service
# Common targets:
multi-user.target # Normal multi-user mode
graphical.target # GUI mode
default.target # Default runlevel
network-online.target # When network is ready
sysinit.target # Early boot
shutdown.target # System shutdown
3. Creating Custom Services
Basic Custom Service Example
Simple Python Application Service
[Unit]
Description=My Python Application
Documentation=https://github.com/user/myapp
After=network.target
Wants=network.target
[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
# Set environment variables
Environment="APP_ENV=production"
Environment="LOG_LEVEL=INFO"
EnvironmentFile=/etc/default/myapp
# Command to execute
ExecStart=/usr/bin/python3 /opt/myapp/main.py
# Reload sends SIGHUP
ExecReload=/bin/kill -HUP $MAINPID
# Restart policy
Restart=on-failure
RestartSec=10
StartLimitIntervalSec=60
StartLimitBurst=3
# Security options
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/lib/myapp
[Install]
WantedBy=multi-user.target
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
SECRET_KEY=changeme
DEBUG=false
Advanced Service Examples
Node.js Application Service
Description=Node.js Application
After=network.target mongodb.service
Requires=mongodb.service
[Service]
Type=simple
User=nodeapp
Group=nodeapp
WorkingDirectory=/var/www/nodeapp
# Using npm start
ExecStart=/usr/bin/npm start
# Or directly with node
# ExecStart=/usr/bin/node /var/www/nodeapp/app.js
# Environment
Environment=NODE_ENV=production
Environment=PORT=3000
# Process management
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Docker Container Service
Description=My Docker Container
Requires=docker.service
After=docker.service network.target
[Service]
Type=simple
# Pull and run container
ExecStartPre=-/usr/bin/docker rm -f mycontainer
ExecStartPre=/usr/bin/docker pull myimage:latest
ExecStart=/usr/bin/docker run \
--name mycontainer \
-p 8080:80 \
-v /data:/app/data \
myimage:latest
ExecStop=/usr/bin/docker stop mycontainer
ExecStopPost=/usr/bin/docker rm -f mycontainer
Restart=always
RestartSec=10
TimeoutStartSec=120
[Install]
WantedBy=multi-user.target
Shell Script Service
Description=Backup Service
After=network.target
[Service]
Type=oneshot
User=backup
Group=backup
WorkingDirectory=/backup
# The actual backup script
ExecStart=/usr/local/bin/backup.sh
# Send email on failure
ExecStopPost=/usr/local/bin/notify-backup-failure.sh
# Log output to syslog
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=backup-service
[Install]
WantedBy=multi-user.target
Timer-based Service
[Unit]
Description=Daily Cleanup Service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup.sh
User=root
# Timer file: /etc/systemd/system/cleanup.timer
[Unit]
Description=Run cleanup daily at 2 AM
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
Service Creation Workflow
$ sudo nano /etc/systemd/system/myapp.service
# Step 2: Create application user (optional)
$ sudo useradd -r -s /bin/false myappuser
# Step 3: Set permissions
$ sudo chown root:root /etc/systemd/system/myapp.service
$ sudo chmod 644 /etc/systemd/system/myapp.service
# Step 4: Reload systemd daemon
$ sudo systemctl daemon-reload
# Step 5: Enable and start service
$ sudo systemctl enable myapp.service
$ sudo systemctl start myapp.service
# Step 6: Verify service status
$ systemctl status myapp.service
$ journalctl -u myapp.service -f
4. Advanced Service Management
Dependency Management
Service Dependencies
$ systemctl list-dependencies nginx.service
$ systemctl list-dependencies --reverse nginx.service
$ systemctl list-dependencies --before nginx.service
$ systemctl list-dependencies --after nginx.service
# Check what requires a service:
$ systemctl list-dependencies --reverse docker.service
# Create dependency chain:
[Unit]
Description=My Application
After=postgresql.service redis.service
Requires=postgresql.service
Wants=redis.service
# After: starts after these services
# Requires: hard dependency (stops if dependency fails)
# Wants: soft dependency (starts if available)
Targets & Runlevels
$ systemctl list-units --type=target
$ systemctl list-unit-files --type=target
# Change default target:
$ sudo systemctl set-default multi-user.target
$ sudo systemctl set-default graphical.target
# Switch to different target:
$ sudo systemctl isolate multi-user.target
$ sudo systemctl isolate rescue.target
# Emergency mode:
$ sudo systemctl emergency
# Rescue mode:
$ sudo systemctl rescue
# Map old runlevels:
# runlevel 3 -> multi-user.target
# runlevel 5 -> graphical.target
Service Logging & Debugging
Journalctl for Services
$ journalctl -u nginx.service
$ journalctl -u nginx.service --since "1 hour ago"
$ journalctl -u nginx.service --since today
$ journalctl -u nginx.service -f # Follow logs
# Filter by priority:
$ journalctl -u nginx.service -p err
$ journalctl -u nginx.service -p warning
$ journalctl -u nginx.service --grep="error"
# Show specific fields:
$ journalctl -u nginx.service -o verbose
$ journalctl -u nginx.service -o json
$ journalctl -u nginx.service --output-fields=MESSAGE,_PID
# Show logs from specific boot:
$ journalctl -u nginx.service -b -1 # Previous boot
Debugging Failed Services
$ systemctl status failed.service
$ systemctl is-failed failed.service
# View detailed failure info:
$ journalctl -u failed.service -xe
$ journalctl -u failed.service --no-pager | tail -50
# Reset failed state:
$ sudo systemctl reset-failed failed.service
# Test service without starting:
$ sudo systemctl dry-run start myservice
# Check service configuration:
$ systemd-analyze verify /etc/systemd/system/myservice.service
$ systemd-analyze cat-config myservice.service
Security & Isolation
Security Directives
# User/Group isolation:
User=appuser
Group=appuser
DynamicUser=yes # Automatic ephemeral user
SupplementaryGroups=www-data,docker
# Filesystem isolation:
ProtectSystem=strict
ReadOnlyPaths=/
ReadWritePaths=/var/lib/myapp
PrivateTmp=true
PrivateDevices=true
NoNewPrivileges=true
# Network isolation:
PrivateNetwork=false
IPAddressDeny=any
IPAddressAllow=localhost
IPAddressAllow=192.168.1.0/24
# Capabilities:
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
# Resource limits:
LimitNOFILE=65536
LimitNPROC=512
LimitMEMLOCK=infinity
Resource Control
# CPU limits:
CPUQuota=50% # Max 50% of one CPU core
CPUWeight=100 # Relative CPU share (1-10000)
CPUShares=1024 # Legacy, use CPUWeight
# Memory limits:
MemoryMax=512M # Hard memory limit
MemoryHigh=256M # Soft memory limit
MemorySwapMax=1G # Swap limit
# I/O limits:
IOWeight=100 # Relative I/O weight
IOReadBandwidthMax=/dev/sda 10M
IOWriteBandwidthMax=/dev/sda 5M
# Block I/O:
BlockIOWeight=100
BlockIOReadBandwidth=/dev/sdb 1M
BlockIOWriteBandwidth=/dev/sdb 1M
# Tasks limit:
TasksMax=100 # Maximum number of tasks
# Check current cgroup:
$ systemctl show --property=ControlGroup nginx.service
5. Troubleshooting & Best Practices
Common Service Issues
Service Won't Start
$ systemctl status myservice
# View logs:
$ journalctl -u myservice -xe
$ journalctl -u myservice --no-pager | tail -100
# Test configuration:
$ systemd-analyze verify /etc/systemd/system/myservice.service
# Check dependencies:
$ systemctl list-dependencies myservice.service
# Manual start for debugging:
$ sudo -u appuser /path/to/command
# Check permissions:
$ ls -la /path/to/command
$ id appuser
Service Crashes/Restarts
$ systemctl show myservice -p NRestarts
# View crash logs:
$ journalctl -u myservice --since "5 minutes ago"
# Check memory limits:
$ systemctl show myservice -p MemoryCurrent
# Adjust restart policy:
[Service]
Restart=on-failure
RestartSec=10
StartLimitIntervalSec=60
StartLimitBurst=3
# Check for signal 9 (OOM killer):
$ dmesg | grep -i "killed process"
# Monitor resource usage:
$ systemd-cgtop
Best Practices
Do's
- Always create a dedicated user for services
- Use
WorkingDirectoryto set proper context - Set appropriate
Restartpolicies - Use
EnvironmentFilefor configuration - Implement proper logging with
StandardOutputandStandardError - Use
systemctl daemon-reloadafter modifying unit files - Test services with
systemctl start --dry-run - Set resource limits to prevent runaway processes
Don'ts
- Don't run services as root unless absolutely necessary
- Don't use
Type=simplefor forking daemons - Don't forget to set
UserandGroup - Don't hardcode secrets in unit files
- Don't use
Restart=alwaysfor oneshot services - Don't modify system-provided unit files directly
- Don't ignore service dependencies
- Don't forget to enable services for auto-start
Service Management Script
Automated Service Deployment Script
# deploy_service.sh - Automated service deployment
deploy_service() {
local SERVICE_NAME="$1"
local SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service"
local APP_USER="appuser"
local APP_DIR="/opt/$SERVICE_NAME"
echo "Deploying service: $SERVICE_NAME"
# Create application user if doesn't exist
if ! id "$APP_USER" &>/dev/null; then
sudo useradd -r -s /bin/false "$APP_USER"
echo "Created user: $APP_USER"
fi
# Create service directory
sudo mkdir -p "$APP_DIR"
sudo chown -R "$APP_USER:$APP_USER" "$APP_DIR"
# Create service file
sudo tee "$SERVICE_FILE" > /dev/null << EOF
[Unit]
Description=$SERVICE_NAME Service
After=network.target
[Service]
Type=simple
User=$APP_USER
Group=$APP_USER
WorkingDirectory=$APP_DIR
ExecStart=/usr/bin/python3 $APP_DIR/app.py
Restart=on-failure
RestartSec=10
Environment="PYTHONUNBUFFERED=1"
[Install]
WantedBy=multi-user.target
EOF
# Set permissions
sudo chmod 644 "$SERVICE_FILE"
# Reload systemd
sudo systemctl daemon-reload
# Enable and start service
sudo systemctl enable "$SERVICE_NAME"
sudo systemctl restart "$SERVICE_NAME"
# Verify deployment
sleep 2
sudo systemctl status "$SERVICE_NAME" --no-pager
}
backup_service() {
local SERVICE_NAME="$1"
local BACKUP_DIR="/backup/services"
local TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Backup service file
if [[ -f "/etc/systemd/system/$SERVICE_NAME.service" ]]; then
cp "/etc/systemd/system/$SERVICE_NAME.service" \
"$BACKUP_DIR/${SERVICE_NAME}_${TIMESTAMP}.service"
fi
echo "Backup created: $BACKUP_DIR/${SERVICE_NAME}_${TIMESTAMP}.service"
}
main() {
case "$1" in
deploy)
deploy_service "$2"
;;
backup)
backup_service "$2"
;;
*)
echo "Usage: $0 {deploy|backup} service_name"
exit 1
;;
esac
}
main "$@"
Systemctl Command Quick Reference
Service Control
$ systemctl stop service
$ systemctl restart service
$ systemctl reload service
$ systemctl enable service
$ systemctl disable service
$ systemctl mask service
$ systemctl unmask service
Service Status
$ systemctl is-active service
$ systemctl is-enabled service
$ systemctl is-failed service
$ systemctl show service
$ systemctl list-units --type=service
$ systemctl list-unit-files --type=service
System Management
$ systemctl reboot
$ systemctl poweroff
$ systemctl halt
$ systemctl suspend
$ systemctl rescue
$ systemctl emergency
$ systemctl set-default target
Logs & Debugging
$ journalctl -u service -f
$ journalctl -u service --since "1 hour ago"
$ journalctl -u service -p err
$ journalctl -xe
$ systemd-analyze verify service
$ systemd-analyze cat-config service
Dependencies & Targets
$ systemctl list-dependencies --reverse service
$ systemctl list-units --type=target
$ systemctl get-default
$ systemctl isolate target
$ systemctl list-dependencies target
Timers & Sockets
$ systemctl list-timers --all
$ systemctl start timer.timer
$ systemctl enable timer.timer
$ systemctl list-sockets
$ systemctl cat timer.timer
$ systemctl status timer.timer
Best Practices Summary
- Use dedicated users: Never run services as root unless absolutely necessary
- Proper restart policies: Choose appropriate Restart= settings based on service type
- Environment files: Store configuration in EnvironmentFile, not in unit files
- Resource limits: Set MemoryMax, TasksMax, and other limits to prevent runaway processes
- Security hardening: Use PrivateTmp, NoNewPrivileges, ProtectSystem, etc.
- Proper logging: Configure StandardOutput and StandardError appropriately
- Test before deploying: Use systemd-analyze verify to check unit files
- Use override files: Don't modify original unit files; use systemctl edit service
- Monitor service health: Set up monitoring for service status and resource usage
- Document services: Include Description and Documentation in unit files
Mastering systemd service management is essential for modern Linux administration. By understanding unit files, dependencies, and proper service configuration, you can ensure reliable, secure, and maintainable service deployments. Always test configurations in a staging environment and implement proper monitoring and alerting for production services.
No comments:
Post a Comment