Table of Contents
- Prerequisites
- Understanding Systemd Service Files
- Step 1: Create a Sample Application
- Step 2: Write the Systemd Service File
- Step 3: Configure Dependencies and Execution Context
- Step 4: Install and Enable the Service
- Step 5: Manage and Monitor the Service
- Step 6: Debugging Common Issues
- Advanced Tips
- Conclusion
- References
Prerequisites
Before diving in, ensure you have the following:
- A Linux system running systemd (check with
systemctl --version). - Basic familiarity with the Linux command line (e.g.,
nano,sudo,chmod). sudoprivileges (to install and manage systemd services).- A text editor (we’ll use
nanofor simplicity, butvimorgeditworks too).
Understanding Systemd Service Files
Systemd services are defined by unit files with a .service extension. These files dictate how the service starts, stops, restarts, and interacts with the system. They are stored in standard directories:
/etc/systemd/system/: For user-defined or custom services (preferred for modifications)./lib/systemd/system/or/usr/lib/systemd/system/: For distribution-provided services (avoid editing these directly).
A systemd service file has three core sections:
1. [Unit]
Defines metadata and dependencies (e.g., when the service should start relative to other services).
2. [Service]
Specifies how the service runs: executable path, user/group, restart policy, and logging.
3. [Install]
Configures how the service is enabled (i.e., which target it belongs to for boot-time activation).
Step 1: Create a Sample Application
To demonstrate systemd service creation, we’ll use a simple Python script that logs timestamps to a file. This helps verify the service is running and producing output.
Create the Script
Save the following as /opt/sample-app/sample-app.py (create the directory first with sudo mkdir -p /opt/sample-app):
#!/usr/bin/env python3
import time
import logging
# Configure logging to write to a file
logging.basicConfig(
filename='/var/log/sample-app/sample-app.log',
level=logging.INFO,
format='%(asctime)s - %(message)s'
)
# Create log directory if it doesn't exist
import os
log_dir = '/var/log/sample-app'
if not os.path.exists(log_dir):
os.makedirs(log_dir, exist_ok=True)
# Simulate a long-running process
try:
while True:
logging.info("Sample application is running...")
time.sleep(5) # Log every 5 seconds
except KeyboardInterrupt:
logging.info("Application stopped manually.")
Make the Script Executable
Set executable permissions and ensure the log directory is writable:
sudo chmod +x /opt/sample-app/sample-app.py
sudo mkdir -p /var/log/sample-app
sudo chown -R $USER:$USER /var/log/sample-app # Temporarily set user ownership (we’ll adjust later)
Test the Script
Run it manually to ensure it works:
/opt/sample-app/sample-app.py
Check /var/log/sample-app/sample-app.log after a few seconds—you should see timestamps. Press Ctrl+C to stop it.
Step 2: Write the Systemd Service File
Now, we’ll create a systemd service file to manage this script.
Create the Service File
Create /etc/systemd/system/sample-app.service with sudo nano /etc/systemd/system/sample-app.service and add the following:
[Unit]
Description=Sample Application - A simple logging service
Documentation=https://example.com/sample-app
After=multi-user.target # Start after the system reaches multi-user mode
[Service]
Type=simple # Service runs in foreground (no forking)
ExecStart=/usr/bin/python3 /opt/sample-app/sample-app.py # Path to executable
User=www-data # Run as non-root user (least privilege)
Group=www-data # Group for the user
Restart=always # Restart on crash, exit, or reboot
WorkingDirectory=/tmp # Working directory for the service
StandardOutput=append:/var/log/sample-app/stdout.log # Redirect stdout
StandardError=append:/var/log/sample-app/stderr.log # Redirect stderr
Environment="PYTHONUNBUFFERED=1" # Disable Python output buffering
UMask=0002 # File permissions: rw-rw-r-- for new files
[Install]
WantedBy=multi-user.target # Start on boot (multi-user runlevel)
Key Directives Explained
| Directive | Purpose |
|---|---|
Description | Human-readable name for the service. |
After | Ensures the service starts after multi-user.target (avoids race conditions). |
Type=simple | The service runs in the foreground (systemd waits for it to start). |
ExecStart | Full path to the executable (use absolute paths to avoid errors). |
User/Group | Runs the service as www-data (non-root) for security. |
Restart=always | Restarts the service if it exits (e.g., crashes, manual stop with systemctl stop除外). |
StandardOutput/Error | Redirects stdout/stderr to log files (optional but useful for debugging). |
Environment | Sets environment variables (e.g., PYTHONUNBUFFERED=1 ensures logs appear immediately). |
WantedBy=multi-user.target | Ensures the service is enabled for the multi-user runlevel (starts on boot). |
Step 3: Configure Dependencies and Execution Context
Systemd services often depend on other services (e.g., a web app needing network.target for internet access). Let’s refine our service with best practices for security and reliability.
Dependencies: After and Requires
After=X.service: Starts the service afterX.service(but doesn’t requireXto run).Requires=X.service: StartsX.serviceif it’s not running, and stops the current service ifXfails.
For example, if our app needed a database, we’d add:
After=mysql.service
Requires=mysql.service
Security: Run as Non-Root
Always run services as a non-root user to limit damage from vulnerabilities. We used User=www-data, but you can create a dedicated user:
sudo useradd -r -s /sbin/nologin sample-app # Create a system user with no login shell
Update the service file:
User=sample-app
Group=sample-app
Log File Permissions
Ensure the sample-app user can write to the log directory:
sudo chown -R sample-app:sample-app /var/log/sample-app
Step 4: Install and Enable the Service
With the service file written, we need to tell systemd about it and configure it to start on boot.
Reload Systemd
Systemd caches unit files, so reload the daemon to detect the new service:
sudo systemctl daemon-reload
Start the Service
Test the service manually before enabling it on boot:
sudo systemctl start sample-app.service
Verify the Service Status
Check if the service is running:
sudo systemctl status sample-app.service
You should see output like this:
● sample-app.service - Sample Application - A simple logging service
Loaded: loaded (/etc/systemd/system/sample-app.service; disabled; vendor preset: enabled)
Active: active (running) since Wed 2024-05-20 12:34:56 UTC; 10s ago
Docs: https://example.com/sample-app
Main PID: 12345 (python3)
Tasks: 1 (limit: 4915)
Memory: 10.0M
CGroup: /system.slice/sample-app.service
└─12345 /usr/bin/python3 /opt/sample-app/sample-app.py
Enable on Boot
To start the service automatically after reboots:
sudo systemctl enable sample-app.service
Output:
Created symlink /etc/systemd/system/multi-user.target.wants/sample-app.service → /etc/systemd/system/sample-app.service.
Step 5: Manage and Monitor the Service
Systemd provides powerful commands to control and monitor services.
Common Service Commands
| Command | Purpose |
|---|---|
sudo systemctl start sample-app | Start the service. |
sudo systemctl stop sample-app | Stop the service. |
sudo systemctl restart sample-app | Restart the service. |
sudo systemctl reload sample-app | Reload configuration (if supported). |
sudo systemctl enable sample-app | Enable on boot. |
sudo systemctl disable sample-app | Disable on boot. |
sudo systemctl is-active sample-app | Check if the service is running. |
sudo systemctl is-enabled sample-app | Check if the service is enabled on boot. |
View Logs
Systemd logs to the journal, a centralized logging system. Use journalctl to view service logs:
# View all logs for the service
sudo journalctl -u sample-app.service
# Follow live logs (like tail -f)
sudo journalctl -u sample-app.service -f
# View logs from the last hour
sudo journalctl -u sample-app.service --since "1 hour ago"
Step 6: Debugging Common Issues
Services can fail for many reasons—here’s how to diagnose and fix them.
1. Service Fails to Start
Check the status for errors:
sudo systemctl status sample-app.service
Common causes:
- Incorrect
ExecStartpath: Verify the path to your script/executable withwhich python3orls /opt/sample-app/sample-app.py. - Permission denied: Ensure the service user has execute permissions on the script and write access to logs.
- Missing dependencies: Use
journalctl -u sample-app.serviceto check for missing libraries (e.g., Python modules).
2. Logs Not Appearing
- Buffering: Python buffers output by default. Use
Environment="PYTHONUNBUFFERED=1"in the service file. - Incorrect log path: Verify
StandardOutput/StandardErrorpaths and permissions.
3. Service Won’t Restart
Check the Restart directive—Restart=always restarts on most exits, but Restart=on-failure only restarts on non-zero exit codes. Use journalctl to see why the service exited.
4. Syntax Errors in Service File
Validate the service file with:
sudo systemd-analyze verify sample-app.service
Advanced Tips
1. Template Services
For managing multiple instances of a service (e.g., app-1, app-2), use template services. Name the file [email protected] and use %i in the service file to reference the instance name:
ExecStart=/usr/bin/python3 /opt/sample-app/sample-app-%i.py
Start with:
sudo systemctl start [email protected]
2. Timers (Cron Alternative)
Use systemd.timer units to run services on a schedule (e.g., daily backups). Create a .timer file alongside your .service file.
3. User-Specific Services
To run a service for a single user (not system-wide), place the .service file in ~/.config/systemd/user/ and use systemctl --user to manage it:
systemctl --user start sample-app.service
systemctl --user enable sample-app.service
Conclusion
Creating systemd services is a foundational skill for Linux administration. By following this tutorial, you’ve learned to:
- Write a systemd service file with
[Unit],[Service], and[Install]sections. - Configure security (non-root users, permissions) and reliability (restart policies, logging).
- Manage and monitor services with
systemctlandjournalctl. - Debug common issues like failed starts and missing logs.
Systemd’s flexibility makes it suitable for everything from small scripts to complex applications. Always test services thoroughly and follow security best practices (e.g., least privilege, logging) to ensure stability.
References
- Systemd Official Documentation
man systemd.service(Service file syntax)man systemd.unit(Unit file basics)man journalctl(Journal logging tool)- DigitalOcean: Understanding Systemd Units and Unit Files