Systemd is the init system used by every major Linux distribution. It's how your system starts, stops, and manages everything from web servers to databases. Whether you're deploying an application or troubleshooting why Nginx won't start, systemd is where you'll spend time. In this lesson, you'll learn to control services, analyze logs, and create your own systemd units.

1. Learning Objectives

By the end of this lesson, you will be able to:

  • Manage services with systemctl (start, stop, restart, enable, disable)
  • View and filter logs with journalctl
  • Create custom systemd service files
  • Understand service dependencies and ordering
  • Use systemd timers instead of cron
  • Troubleshoot failed services

2. Why This Matters

Real-world scenario: Your Python web application needs to start automatically when the server boots, restart if it crashes, and log all output to a centralized location. Systemd handles all of this with a simple service file.

What systemd replaced:

  • SysV init (complex shell scripts)
  • Upstart (Ubuntu's former init)
  • Cron (for scheduled tasks - replaced by timers)

3. Core Concepts

Systemctl: Service Management

# Basic service commands
systemctl status nginx           # Check if service is running
systemctl start nginx            # Start service
systemctl stop nginx             # Stop service
systemctl restart nginx          # Restart service
systemctl reload nginx           # Reload configuration (without restart)
systemctl enable nginx           # Start at boot
systemctl disable nginx          # Don't start at boot

# Check service state
systemctl is-active nginx        # Check if running (returns active/inactive)
systemctl is-enabled nginx       # Check if enabled at boot
systemctl is-failed nginx        # Check if failed

# List services
systemctl list-units --type=service           # All running services
systemctl list-units --type=service --all     # All services (including stopped)
systemctl list-unit-files --type=service      # All service files

# Manage system
systemctl reboot                 # Reboot the system
systemctl poweroff               # Shutdown
systemctl suspend                # Suspend to RAM
systemctl hibernate              # Hibernate to disk
systemctl commands
kubectl get pods -w
$ sudo systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2025-01-17 10:00:00 UTC; 2 days ago
Docs: man:nginx(8)
Process: 1234 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 1235 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 1236 (nginx)
Tasks: 3 (limit: 2345)
Memory: 8.2M
CPU: 45ms
CGroup: /system.slice/nginx.service
├─1236 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
├─1237 nginx: worker process
└─1238 nginx: worker process

Warning: Some lines were ellipsized, use -l to show in full.
systemctl status output

Journalctl: Log Management

# Basic log viewing
journalctl                     # All logs (uses less pager)
journalctl -xe                 # Show last logs and explain
journalctl -f                  # Follow logs (like tail -f)
journalctl -n 50               # Last 50 lines

# Filter by service
journalctl -u nginx            # Logs for nginx service
journalctl -u nginx -u mysql   # Multiple services

# Filter by time
journalctl --since "2025-01-17"
journalctl --since "1 hour ago"
journalctl --since yesterday
journalctl --since "2025-01-17 10:00:00" --until "2025-01-17 12:00:00"

# Filter by priority
journalctl -p err               # Errors only
journalctl -p 3                 # Same (0=emerg, 1=alert, 2=crit, 3=err, 4=warning, 5=notice, 6=info, 7=debug)
journalctl -p err -p warning    # Errors and warnings

# Output formats
journalctl -o json              # JSON format
journalctl -o verbose           # Verbose with all fields
journalctl -o cat               # Just message (no metadata)

# Disk usage
journalctl --disk-usage

# Maintenance
journalctl --vacuum-size=500M   # Keep logs under 500MB
journalctl --vacuum-time=30d    # Keep logs for 30 days
journalctl log management

Creating Custom Service Files

# Service file location
# /etc/systemd/system/myapp.service

sudo nano /etc/systemd/system/myapp.service
Create service file
[Unit]
Description=My Python Web Application
After=network.target mysql.service
Requires=mysql.service
Wants=mysql.service
Documentation=https://example.com/docs

[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/myapp
Environment="APP_ENV=production"
Environment="DB_HOST=localhost"
EnvironmentFile=/etc/myapp/secrets.env

# Start command
ExecStart=/usr/bin/python3 /opt/myapp/app.py
ExecStop=/usr/bin/kill -TERM $MAINPID
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10

# Process limits
LimitNOFILE=65536
MemoryMax=1G
CPUQuota=200%

# Security
PrivateTmp=yes
NoNewPrivileges=yes
ProtectSystem=strict
ReadWritePaths=/var/log/myapp /var/lib/myapp

[Install]
WantedBy=multi-user.target
Complete systemd service file
# After creating the service file
sudo systemctl daemon-reload   # Reload systemd to read new service
sudo systemctl start myapp     # Start service
sudo systemctl enable myapp    # Enable at boot
sudo systemctl status myapp    # Check status

# View logs
journalctl -u myapp -f

# Check for errors
sudo systemctl status myapp -l  # Full status with details
Loading and starting custom service

Systemd Timers (Better Than Cron)

# Timer file: /etc/systemd/system/backup.timer

[Unit]
Description=Run backup daily
Requires=backup.service

[Timer]
OnCalendar=daily
OnCalendar=*-*-* 02:00:00      # Run at 2 AM daily
OnCalendar=Mon..Fri 09:00:00   # Weekdays at 9 AM
Persistent=true                 # Run missed timers after boot

# Alternative time specs
# OnBootSec=15min               # 15 minutes after boot
# OnUnitActiveSec=1h            # 1 hour after last activation
# RandomizedDelaySec=30min      # Random delay between 0-30min

[Install]
WantedBy=timers.target

# Service file: /etc/systemd/system/backup.service
[Unit]
Description=Backup script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal

[Install]
WantedBy=multi-user.target
Systemd timer configuration
# Enable and start timer
sudo systemctl daemon-reload
sudo systemctl enable backup.timer
sudo systemctl start backup.timer

# Manage timers
systemctl list-timers          # List all active timers
systemctl list-timers --all    # Including inactive
systemctl status backup.timer  # Check timer status

# Test timer
systemctl start backup.service  # Run service manually
Managing systemd timers

4. Hands-on Practice Lab

Project: Create a Simple Web App Service

# Create simple Python web app
sudo mkdir -p /opt/sampleapp
sudo cat > /opt/sampleapp/app.py << 'EOF'
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import datetime

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write(f"""
        <html>
            <body>
                <h1>Hello from Systemd!</h1>
                <p>Time: {datetime.datetime.now()}</p>
                <p>Hostname: {self.server.server_name}</p>
            </body>
        </html>
        """.encode())

if __name__ == '__main__':
    server = HTTPServer(('0.0.0.0', 8080), Handler)
    print('Starting server on port 8080...')
    server.serve_forever()
EOF

sudo chmod +x /opt/sampleapp/app.py

# Create system user
sudo useradd -r -s /bin/false sampleapp

# Create service file
sudo cat > /etc/systemd/system/sampleapp.service << 'EOF'
[Unit]
Description=Sample Python Web App
After=network.target

[Service]
Type=simple
User=sampleapp
Group=sampleapp
WorkingDirectory=/opt/sampleapp
ExecStart=/usr/bin/python3 /opt/sampleapp/app.py
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# Start the service
sudo systemctl daemon-reload
sudo systemctl start sampleapp
sudo systemctl enable sampleapp
sudo systemctl status sampleapp

# Test the app
curl http://localhost:8080
Complete sample web app service

5. Debugging Failed Services

# When a service fails to start
sudo systemctl status myservice

# Common issues and fixes

# 1. Permission issues - check user/group
ls -la /opt/myapp/
# Fix: chown -R appuser:appgroup /opt/myapp/

# 2. Environment variables missing
sudo systemctl show myservice | grep Environment
# Fix: Add Environment= lines or EnvironmentFile=

# 3. Working directory issues
# Fix: Add WorkingDirectory=/path/to/app

# 4. Port already in use
sudo netstat -tulpn | grep :8080
# Fix: Change port or stop conflicting service

# 5. Syntax error in service file
sudo systemd-analyze verify /etc/systemd/system/myservice.service

# View full logs
journalctl -u myservice -n 50 --no-pager
journalctl -u myservice -f  # Follow logs in real-time
Debugging service failures

6. Common Errors & Solutions

7. Summary Checklist

8. Next Steps

Next category: Bash Scripting - Conditionals, Loops, and Functions

You'll learn to:

  • Make decisions with if/elif/else
  • Loop with for and while
  • Create reusable functions
  • Build automation scripts

Gataya Med

DevOps Engineer & Backend Developer. Sharing insights on cloud, automation, and scalable systems.

Comments (0)

Sarah Chen January 20, 2025

This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!

Reply

Leave a Comment