Linux process management is surprisingly simple once you realize that systemd isn’t just a boot loader, but a state machine that manages everything.
Let’s see systemd in action managing a simple service. Imagine we have a tiny web server we want to run.
First, we need a service unit file. This tells systemd how to start, stop, and manage our service.
# /etc/systemd/system/my-webserver.service
[Unit]
Description=My Tiny Web Server
After=network.target
[Service]
ExecStart=/usr/local/bin/my-webserver
Restart=on-failure
User=www-data
Group=www-data
[Install]
WantedBy=multi-user.target
Here, Description is a human-readable name. After=network.target means our service should start after the network is up. ExecStart is the command to run. Restart=on-failure is crucial – if the server crashes, systemd will try to restart it. User and Group specify who runs the process, enhancing security. WantedBy=multi-user.target means this service should be started when the system reaches its normal multi-user runlevel.
Now, let’s make sure our web server executable is in place and has the right permissions:
sudo apt-get update
sudo apt-get install -y python3-flask # Or your preferred web server
echo 'from flask import Flask; app = Flask(__name__); @app.route("/")\ndef hello(): return "Hello from my web server!"\nif __name__ == "__main__": app.run(host="0.0.0.0", port=8080)\n' | sudo tee /usr/local/bin/my-webserver
sudo chmod +x /usr/local/bin/my-webserver
We’ll use Flask for this example. The Python code defines a simple web server listening on port 8080.
After creating the service file, we need to tell systemd to reload its configuration and then start our new service:
sudo systemctl daemon-reload
sudo systemctl start my-webserver.service
To check its status, we use systemctl status:
sudo systemctl status my-webserver.service
You’ll see output like this, showing it’s active and running, along with recent log entries:
● my-webserver.service - My Tiny Web Server
Loaded: loaded (/etc/systemd/system/my-webserver.service; disabled; vendor preset: enabled)
Active: active (running) since Mon 2023-10-27 10:30:00 UTC; 5s ago
Main PID: 12345 (python3)
Tasks: 1 (limit: 4915)
Memory: 10.5M
CPU: 15ms
CGroup: /system.slice/my-webserver.service
└─12345 /usr/bin/python3 /usr/local/bin/my-webserver
Oct 27 10:30:00 your-hostname systemd[1]: Started My Tiny Web Server.
Now, you can access it from your browser or curl:
curl http://localhost:8080/
Output:
Hello from my web server!
To make it start on boot, we enable it:
sudo systemctl enable my-webserver.service
This creates a symlink from /etc/systemd/system/multi-user.target.wants/ to our service file.
The internal model of systemd is a dependency graph and a state machine. Each unit (service, socket, timer, etc.) is a node in the graph. systemd starts units based on their dependencies (After=, Requires=, Wants=) and transitions them through states (inactive, activating, active, deactivating, failed). When you run systemctl start, systemd identifies all the units that need to be started to satisfy the dependencies of your requested unit and then executes them in the correct order. The Restart=on-failure directive tells systemd to monitor the process’s exit code. If it’s non-zero (indicating an error), systemd will attempt to restart the service, respecting any retry delays defined in the unit file or system defaults. This makes systemd a robust supervisor that handles process lifecycle management automatically.
The one thing most people don’t realize is that systemd isn’t just starting processes; it’s actively managing their state. It knows if a service is supposed to be running, if it’s currently running, and what to do if it deviates from its desired state. This state-driven approach is what makes it powerful for managing complex systems. It’s not just a script runner; it’s a system orchestrator.
The next step is to explore how systemd manages more complex scenarios like socket activation or timer units.