Introduction / Why This Is Needed
Systemd is a modern init system and service manager for most Linux distributions. Creating your own service (daemon) allows you to automatically run your scripts or programs at system boot, manage their lifecycle (start, stop, restart), centralize logging output, and ensure recovery from failures. This is the standard and reliable way to deploy background tasks in Linux.
After completing this guide, you will be able to create a working service for any executable program (Python script, bash script, binary file) that will:
- Automatically start when the OS boots.
- Shut down gracefully on system shutdown.
- Write logs to
journald(the system journal). - Restart after a crash.
Requirements / Preparation
Before you begin, ensure that:
- You have terminal access with sudo privileges (for system services) or access to your home directory (for user services).
- Systemd is installed on your system (relevant for all modern distributions: Ubuntu 22.04+, Debian 11+, CentOS 8/RHEL 8+, Fedora 35+).
- The executable file or script you want to service is already prepared, manually tested, and has execute permissions (
chmod +x /path/to/file). - You know the absolute path to this executable file.
Step 1: Preparing the Executable Script or Program
Assume you have a simple bash script for monitoring disk space that should run in the background. It is already created and located at /opt/scripts/disk_monitor.sh.
#!/bin/bash
# Example script /opt/scripts/disk_monitor.sh
while true; do
DATE=$(date '+%Y-%m-%d %H:%M:%S')
df -h / | tail -n1 | awk '{print "ROOT: " $5 " used"}' >> /var/log/disk_monitor.log
sleep 300
done
Test it manually:
sudo chmod +x /opt/scripts/disk_monitor.sh
sudo /opt/scripts/disk_monitor.sh
Press Ctrl+C to stop it for testing. Ensure entries appear in /var/log/disk_monitor.log.
Step 2: Creating the Unit File
Create the service file. For a system service (running as root or a specified user), use /etc/systemd/system/.
sudo nano /etc/systemd/system/disk-monitor.service
💡 Tip: The filename usually matches the service name. Use lowercase letters, numbers, and hyphens. A dot in the name is not required but permissible.
Step 3: Writing the Basic Unit and Service Configuration
Paste the following configuration into the editor. It covers most basic scenarios.
[Unit]
Description=Service for monitoring free space on the root partition
Documentation=man:df(1)
After=network.target
[Service]
Type=simple
ExecStart=/opt/scripts/disk_monitor.sh
Restart=on-failure
RestartSec=10
User=root
Group=root
[Install]
WantedBy=multi-user.target
Section Breakdown:
[Unit]— metadata and dependencies.Description— a brief description of the service (shown insystemctl status).After— indicates the service should start after the network is up (network.target). For non-network services, you can remove this or specifylocal-fs.target(after local filesystems are mounted).
[Service]— main process configuration.Type=simple(default) — systemd assumes the process specified inExecStartis the main one and runs in the foreground. Suitable for most scripts and modern programs.ExecStart— the absolute path to the command/script to run. All arguments are specified here.Restart=on-failure— automatically restart the service if it exits with a non-zero return code.RestartSec=10— wait 10 seconds before restarting.User/Group— the user/group to run the process as. It is safer to specify a non-root user (e.g.,User=monitoruser) unless root privileges are necessary. For system utilities, root may be required.
[Install]— instructions forsystemctl enable.WantedBy=multi-user.target— creates a symlink in/etc/systemd/system/multi-user.target.wants/, meaning "start at boot in multi-user mode (standard)." For graphical services,graphical.targetmay be used.
Save the file (Ctrl+O, Enter) and exit the editor (Ctrl+X).
Step 4: Configuring Autostart and Initialization
After creating or modifying a unit file, you must reload the systemd configuration:
sudo systemctl daemon-reload
Now enable the service for autostart (creates the symlink):
sudo systemctl enable disk-monitor.service
Output: Created symlink /etc/systemd/system/multi-user.target.wants/disk-monitor.service → /etc/systemd/system/disk-monitor.service.
Step 5: Starting and Checking Status
Start the service manually:
sudo systemctl start disk-monitor.service
Check its status:
sudo systemctl status disk-monitor.service
Expected output (simplified):
● disk-monitor.service - Service for monitoring free space on the root partition
Loaded: loaded (/etc/systemd/system/disk-monitor.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2026-02-15 14:30:00 MSK; 5s ago
Main PID: 12345 (bash)
Tasks: 1 (limit: 4915)
Memory: 1.2M
CGroup: /system.slice/disk-monitor.service
└─12345 /bin/bash /opt/scripts/disk_monitor.sh
Key lines:
Loaded— file is loaded,enabled— enabled for autostart.Active: active (running)— service is running.Main PID— PID of the main process.CGroup— the cgroup the process is placed in.
Step N: Additional Management Operations
You can now manage the service with standard commands:
- Stop:
sudo systemctl stop disk-monitor.service - Restart:
sudo systemctl restart disk-monitor.service(use if you changed the config). - Reload:
sudo systemctl reload disk-monitor.service(if the service supports SIGHUP to reload config without full stop). - View logs:
sudo journalctl -u disk-monitor.service -f(-f— follow new entries in real time). - Check configuration syntax:
sudo systemctl daemon-reload(runs automatically onenable/start, but useful after manual file edits).
Verifying the Result
- Autostart: Reboot the system (
sudo reboot). After logging in, check the service status:systemctl status disk-monitor.service. It should beactive (running). - Logging: Ensure logs are being written:
sudo tail -f /var/log/disk_monitor.logor via journald:sudo journalctl -u disk-monitor.service. - Automatic recovery: Forcefully terminate the process (e.g.,
sudo kill <PID>from thestatusoutput). Check the status again after a few seconds — systemd should have restarted it.
Common Issues
Issue: systemctl status shows failed (Result: exit-code).
Solution: Check the error details in the same status output (the ... line). Most often this is:
- Incorrect path in
ExecStart. Ensure the path is absolute and the file exists. - Missing execute permissions.
sudo chmod +x /path/to/script. - Error inside the script. Run the script manually as the same user (
sudo -u <user> /path/to/script) and check the output. - Dependencies. If the script needs network or mounted disks, check
After=andRequires=in the[Unit]section.
Issue: Service is not enabled for autostart (systemctl is-enabled returns disabled).
Solution: Run sudo systemctl enable disk-monitor.service again after daemon-reload. Ensure the [Install] section has WantedBy= or RequiredBy=.
Issue: Service logs are empty in journalctl.
Solution: By default, systemd captures stdout/stderr. If the script writes directly to a file (as in the example), its output won't appear in journald. This is normal. For journald logging, remove the file redirection from the script and configure log rotation with journalctl, or use StandardOutput=append:/var/log/disk_monitor.log in the [Service] section.
Issue: Service starts but immediately exits.
Solution: This often happens with Type=simple if the main process exits. For long-running scripts, ensure they have an infinite loop or wait (sleep). If the program daemonizes itself (forks), you may need Type=forking and PIDFile=/var/run/name.pid. But for simple scripts with a loop, Type=simple is the correct choice.