Table of Contents
- Understanding Systemd vs. Traditional Init Systems
- Pre-Migration Checklist
- Step-by-Step Migration Process
- Post-Migration Verification
- Troubleshooting Common Issues
- Conclusion
- References
1. Understanding Systemd vs. Traditional Init Systems
Before diving into migration, it’s critical to understand how systemd differs from legacy init systems.
SysVinit: The Legacy Approach
SysVinit uses runlevels (0-6) to define system states (e.g., runlevel 3 = multi-user text mode, runlevel 5 = graphical mode) and relies on shell scripts (/etc/init.d/) to start/stop services sequentially. Key limitations:
- Slow boot times (services start one after another).
- No built-in dependency management (scripts must handle dependencies manually).
- Limited logging (relies on external tools like
syslog).
Upstart: A Partial Improvement
Upstart (used in older Ubuntu and Fedora releases) introduced event-based activation but remained constrained by compatibility with SysVinit. It lacked a unified framework for service management and was eventually replaced by systemd.
Systemd: The Modern Standard
Systemd is a suite of tools (not just an init system) that manages system boot, services, logging, and more. Key advantages:
- Parallelization: Starts services simultaneously to reduce boot time.
- Unified management: Controls services, sockets, mounts, timers, and more via “units.”
- Dependency handling: Automatically resolves and orders service dependencies.
- Journald: A centralized logging system with structured, queryable logs.
- On-demand activation: Starts services only when needed (via sockets or timers).
2. Pre-Migration Checklist
Migrating to systemd requires careful planning to avoid downtime. Use this checklist to prepare:
Backup Critical Data
- Create a full system backup (e.g., with
rsync,dd, or a tool like Clonezilla). - Backup
/etc/init.d/(SysVinit scripts),/etc/default/(service defaults), and/etc/inittab(runlevel configs).
Check Compatibility
- Hardware/Drivers: Ensure your kernel (≥3.0 recommended) and drivers support systemd. Most modern hardware works, but older or specialized devices may need testing.
- Applications: Verify that critical apps (e.g., databases, web servers) are compatible with systemd. Check vendor documentation for known issues.
Document Current Setup
- Inventory running services:
ls /etc/init.d/(SysVinit) orinitctl list(Upstart). - Note runlevels:
grep id: /etc/inittab(default runlevel). - Map dependencies: Which services rely on others (e.g.,
apache2depends onnetworking)?
Set Up a Staging Environment
Test migration in a non-production environment first (e.g., a VM or spare server). Replicate your production setup to catch issues early.
3. Step-by-Step Migration Process
3.1 Preparing the Environment
Update Your System
Start by updating your existing system to ensure compatibility with systemd packages:
# Debian/Ubuntu
sudo apt update && sudo apt upgrade -y
# RHEL/CentOS
sudo yum update -y
Install Systemd
Most modern distributions include systemd by default, but if you’re migrating from SysVinit, install the required packages:
-
Debian/Ubuntu:
sudo apt install systemd-sysv # Replaces SysVinit with systemd -
RHEL/CentOS:
sudo yum install systemd # Systemd is pre-installed on RHEL 7+, but verify -
Arch Linux:
sudo pacman -S systemd # Pre-installed, but ensure it’s up to date
Note: On some systems, you may need to remove conflicting packages (e.g., sysvinit-core on Debian) before installing systemd.
3.2 Understanding Systemd Units
Systemd manages resources via units—configuration files that define services, targets, sockets, and more. Units are stored in:
/usr/lib/systemd/system/(distro-provided units)./etc/systemd/system/(user/custom units, overriding distro defaults).
Common Unit Types
| Unit Type | Purpose | Example Filename |
|---|---|---|
.service | Manages a daemon or service. | nginx.service |
.target | Groups units (like “runlevels”). | multi-user.target |
.socket | Activates services on network activity. | sshd.socket |
.mount | Controls filesystem mounts. | mnt-data.mount |
.timer | Schedules tasks (cron alternative). | backup.timer |
Structure of a .service File
A .service unit defines how to start/stop a service. It has three main sections:
[Unit]
Description=Example Service
After=network.target # Start AFTER network is up
Requires=mysql.service # Fail if mysql isn’t running
[Service]
User=www-data
ExecStart=/usr/bin/example-daemon --config /etc/example.conf
Restart=always # Restart if the service crashes
RestartSec=5 # Wait 5s before restarting
[Install]
WantedBy=multi-user.target # Start when multi-user.target is active
- [Unit]: Metadata (description, dependencies).
- [Service]: Service behavior (start command, user, restart policy).
- [Install]: How to enable the service (which target to attach to).
3.3 Migrating SysVinit Scripts to Systemd Services
SysVinit scripts (in /etc/init.d/) can be migrated to systemd services in two ways:
Temporary: Use systemd-sysv-generator
Systemd automatically converts SysVinit scripts to temporary .service units at boot via systemd-sysv-generator. These units work but lack systemd’s advanced features (e.g., restart policies). To check generated units:
systemctl list-unit-files --type=service | grep generated
Permanent: Write Native Systemd Services
For full integration, replace SysVinit scripts with native .service units. Follow these steps:
Step 1: Analyze the SysVinit Script
Identify:
- Start/stop commands (e.g.,
start() { /usr/bin/nginx start; }). - Dependencies (e.g.,
Required-Start: $network $remote_fs). - Runlevels (e.g.,
Default-Start: 2 3 4 5→ maps tomulti-user.target).
Step 2: Create a .service File
Use the SysVinit script details to write a .service file. For example, migrate an example script:
SysVinit Script (/etc/init.d/example):
#!/bin/sh
### BEGIN INIT INFO
# Provides: example
# Required-Start: $network $syslog
# Required-Stop: $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Example daemon
### END INIT INFO
start() { /usr/bin/example start; }
stop() { /usr/bin/example stop; }
case "$1" in start|stop) "$1";; esac
Equivalent Systemd Service (/etc/systemd/system/example.service):
[Unit]
Description=Example daemon
After=network.target syslog.target # "Required-Start" → After/dependencies
Requires=network.target syslog.target
[Service]
Type=simple # Most common; runs ExecStart directly
ExecStart=/usr/bin/example start
ExecStop=/usr/bin/example stop
Restart=on-failure # Restart if it fails (better than SysVinit)
[Install]
WantedBy=multi-user.target # "Default-Start: 2-5" → multi-user.target
Step 3: Validate and Reload
Check the unit for errors:
systemd-analyze verify example.service
Reload systemd to detect the new unit:
sudo systemctl daemon-reload
3.4 Configuring Systemd Targets
Targets are groups of units that define system states (like runlevels). Use them to control which services start at boot.
Common Targets
| Target | Purpose | Equivalent Runlevel |
|---|---|---|
multi-user.target | Multi-user text mode (no GUI). | Runlevel 3 |
graphical.target | Multi-user with GUI. | Runlevel 5 |
rescue.target | Single-user mode for recovery. | Runlevel 1 |
emergency.target | Minimal shell (no services). | N/A |
poweroff.target | Shutdown the system. | Runlevel 0 |
Set the Default Target
To set the default boot target (e.g., multi-user.target):
sudo systemctl set-default multi-user.target
Verify with:
systemctl get-default # Output: multi-user.target
3.5 Enabling and Managing Services
Systemd uses systemctl to manage services. Key commands:
| Task | Command |
|---|---|
| Start a service immediately | sudo systemctl start example.service |
| Stop a service immediately | sudo systemctl stop example.service |
| Restart a service | sudo systemctl restart example.service |
| Enable on boot | sudo systemctl enable example.service |
| Disable on boot | sudo systemctl disable example.service |
| Check status | systemctl status example.service |
| List all running services | systemctl list-units --type=service --state=running |
3.6 Setting Up Logging with Journald
Systemd includes journald, a centralized logging daemon that replaces traditional syslog. Journal logs are stored in /var/log/journal/ (persistent) or memory (volatile).
Basic Journalctl Commands
| Task | Command |
|---|---|
| View all logs (newest last) | journalctl |
| View logs for a service | journalctl -u example.service |
| Follow real-time logs | journalctl -u example.service -f |
| View logs from the last boot | journalctl -b |
| Filter by time (e.g., last hour) | journalctl --since "1 hour ago" |
| Filter by priority (e.g., errors) | journalctl -p err |
Make Logs Persistent
By default, logs may be volatile. To enable persistence:
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
4. Post-Migration Verification
After migrating, verify your systemd setup with these checks:
Check Boot Target
Ensure the system boots to the correct target:
systemctl get-default # Should match your desired target (e.g., multi-user.target)
Verify Services
List enabled services and confirm critical ones are running:
systemctl list-unit-files --type=service --state=enabled
systemctl list-units --type=service --state=running
Check Logs for Errors
Search journald for migration-related issues:
journalctl --since "today" | grep -i error
Test Reboot
Reboot the system and ensure it boots successfully:
sudo reboot
After reboot, verify services start automatically:
systemctl is-active example.service # Should return "active"
5. Troubleshooting Common Issues
Service Fails to Start
- Check the service status for details:
systemctl status example.service - View logs for the service:
journalctl -u example.service --no-pager - Common fixes: Correct
ExecStartpaths, resolve missing dependencies, or fix permissions on the executable.
System Boots to Emergency Mode
This usually indicates a critical unit failure (e.g., a corrupted mount unit). Check logs in emergency mode:
journalctl -b # View boot logs
Journald Logs Not Persistent
Ensure /var/log/journal exists and has correct permissions:
sudo mkdir -p /var/log/journal
sudo chmod 2755 /var/log/journal
sudo systemctl restart systemd-journald
SysVinit Scripts Not Generating Units
If systemd-sysv-generator isn’t creating units, ensure the script has valid LSB headers (the ### BEGIN INIT INFO block).
6. Conclusion
Transitioning to systemd may seem daunting, but its modern features—parallel boot, unified management, and robust logging—make it well worth the effort. By following this guide, you’ve learned to:
- Prepare your system for migration.
- Convert SysVinit scripts to native systemd services.
- Configure targets and manage services with
systemctl. - Troubleshoot common post-migration issues.
Systemd is now the industry standard, and mastering it will simplify system administration for years to come. For advanced use cases (e.g., timers, socket activation, or custom targets), refer to the official documentation linked below.