Introduction
Systemd is the default init system on most modern Linux distributions, such as Ubuntu, Fedora, and CentOS. It manages system services, daemons, and scripts during boot, runtime, and shutdown. This guide focuses on designing and creating systemd unit files for startup and shutdown scripts, providing explicit details, step-by-step instructions, and practical examples.
Startup scripts run when the system boots up, while shutdown scripts execute during system halt or reboot. Systemd uses unit files (e.g., .service) to define these behaviors.
Prerequisites: Basic Linux knowledge, root access, and a text editor like vim or nano.
Systemd Basics
Systemd organizes everything into "units." The most common for scripts is the .service unit, which can define executables to run at specific times.
- Unit File Location: Place custom units in
/etc/systemd/system/for persistence across reboots. - Basic Structure: A unit file has sections like [Unit], [Service], and [Install].
- Commands:
systemctl daemon-reload: Reload systemd after editing units.systemctl enable <unit>: Enable at boot.systemctl start/stop <unit>: Manual control.systemctl status <unit>: Check status.
Example of a minimal service unit:
[Unit]
Description=My Simple Service
[Service]
ExecStart=/path/to/my-script.sh
[Install]
WantedBy=multi-user.target
Designing and Creating Startup Scripts
Startup scripts are services that start automatically when the system reaches a certain target (e.g., multi-user.target for console boot or graphical.target for GUI).
Step-by-Step Design
- Define Purpose: What does the script do? E.g., start a web server, mount a drive, or run a backup.
- Choose Type: Use
Type=simplefor foreground processes,Type=oneshotfor scripts that run once and exit. - Dependencies: Use
After=orRequires=to ensure order (e.g., after network). - Enable at Boot: Add [Install] section with
WantedBy=. - Test: Reload, enable, and reboot to verify.
Example: Simple Startup Script for Logging System Info
Create a script /usr/local/bin/log-system-info.sh:
#!/bin/bash
echo "System booted at $(date)" >> /var/log/boot.log
uname -a >> /var/log/boot.log
Make it executable: chmod +x /usr/local/bin/log-system-info.sh
Unit file /etc/systemd/system/log-system-info.service:
[Unit]
Description=Log System Info at Startup
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/log-system-info.sh
RemainAfterExit=true
[Install]
WantedBy=multi-user.target
Activate: systemctl daemon-reload; systemctl enable log-system-info.service; systemctl start log-system-info.service
Advanced Example: Startup Daemon
For a persistent process like a custom server:
[Unit]
Description=My Custom Daemon
After=network-online.target
[Service]
Type=simple
ExecStart=/path/to/my-daemon --foreground
Restart=always
User=root
[Install]
WantedBy=graphical.target
Designing and Creating Shutdown Scripts
Shutdown scripts run before the system halts. Use services that activate during shutdown.target, often with Before=shutdown.target or ExecStop= in regular services.
Step-by-Step Design
- Identify Actions: E.g., save data, unmount drives, or send notifications.
- Use ExecStop: For services, define cleanup in ExecStop.
- Standalone Shutdown Unit: Use Type=oneshot with Conflicts=shutdown.target and Before=shutdown.target.
- Timing: Ensure it runs early using Before=umount.target or final.target.
- Test: Use
systemctl stop <unit>or reboot.
Example: Simple Shutdown Script for Backup
Script /usr/local/bin/backup-on-shutdown.sh:
#!/bin/bash
echo "System shutting down at $(date)" >> /var/log/shutdown.log
rsync -a /important/data /backup/location
Make executable: chmod +x /usr/local/bin/backup-on-shutdown.sh
Unit file /etc/systemd/system/backup-on-shutdown.service:
[Unit]
Description=Backup Data on Shutdown
DefaultDependencies=no
Before=shutdown.target umount.target final.target
[Service]
Type=oneshot
ExecStart=/bin/true # No-op for start
ExecStop=/usr/local/bin/backup-on-shutdown.sh
RemainAfterExit=true
[Install]
WantedBy=multi-user.target
Note: The service "starts" with a no-op but executes ExecStop on shutdown. Activate as above.
Advanced Example: Cleanup in a Running Service
For a service that needs cleanup:
[Unit]
Description=My App Service
[Service]
ExecStart=/path/to/my-app
ExecStop=/path/to/cleanup-script.sh
KillSignal=SIGTERM
[Install]
WantedBy=multi-user.target
Best Practices
- Use descriptive names and descriptions in unit files.
- Handle errors in scripts with proper logging (e.g., to journalctl).
- Avoid infinite loops; use timeouts like
TimeoutStopSec=30. - Test in a virtual machine to prevent boot issues.
- Use
systemd-analyze blameto debug boot times. - Secure scripts: Run as non-root user if possible with
User=andGroup=.
Troubleshooting
- Service Fails to Start: Check
journalctl -u <unit>for logs. - Not Running at Boot: Ensure enabled and check
systemctl list-unit-files. - Shutdown Script Not Executing: Verify Before= dependencies and test with
systemctl poweroff. - Conflicts: Use
systemctl show <unit>to inspect.