Manage systemd Services with systemctl: A Guide
Start, stop, enable, and troubleshoot Linux services with systemctl. Plus how to write a custom systemd unit file and read logs with journalctl.
On modern Ubuntu, almost everything that runs in the backgroundโyour web server, SSH daemon, database, the cron replacement, even mounting filesystemsโis managed by systemd. The command you use to talk to it is systemctl. Knowing a handful of systemctl verbs is the difference between confidently operating a Linux server and guessing why a service will not start.
This guide is a practical tour of managing services with systemctl on Ubuntu 24.04 LTS: starting and stopping services, enabling them at boot, reading their status, writing your own custom unit file, and reading logs with journalctl. Every command is copy-pasteable and safe to run on a real server.
Prerequisites
- A machine running Ubuntu 24.04 LTS.
- A user with
sudoprivileges. - Basic comfort editing files from the terminal.
Understanding units
systemd manages units. A service (a background process) is a unit of type .service; there are also .socket, .timer, .mount, and .target units. When you type systemctl status ssh, systemd assumes the .service suffix. List all loaded service units:
systemctl list-units --type=serviceTo see every installed unit and whether it starts at boot:
systemctl list-unit-files --type=serviceChecking service status
The most-used command. It shows whether a service is running, its main PID, memory use, and the last few log lines:
systemctl status sshโ ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-06-20 09:12:03 UTC; 2h 4min ago
Main PID: 812 (sshd)
Memory: 5.2MTwo words matter here. active (running) tells you the current state. enabled tells you it will start at the next boot. Those are independentโa service can be running now but disabled at boot, or vice versa.
For scripting, query just the active state, which sets a useful exit code:
systemctl is-active ssh
systemctl is-enabled sshStarting, stopping, and restarting
These change the current runtime state and take effect immediately:
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginxWhen you change a service's configuration, prefer reload over restart if the service supports itโreload re-reads config without dropping connections:
sudo systemctl reload nginxIf you are unsure whether a service can reload, reload-or-restart does the right thing:
sudo systemctl reload-or-restart nginxEnabling and disabling at boot
enable creates the symlinks that make a service start at boot; disable removes them. Neither changes the current state. To do both at once, add --now:
sudo systemctl enable --now nginx
sudo systemctl disable --now nginxTo stop a service from ever being startedโeven as a dependency of another unitโmask it. This links it to /dev/null:
sudo systemctl mask nginx
sudo systemctl unmask nginxWriting a custom systemd unit
Suppose you have a small Python application at /opt/myapp/app.py that you want to run as a managed serviceโrestarted automatically if it crashes, started at boot. Create a unit file:
sudo nano /etc/systemd/system/myapp.servicePlace your unit definitions under /etc/systemd/system/; that directory is for local admin-defined units and overrides packaged ones. Add:
[Unit] Description=My Python Application After=network.target[Service] Type=simple User=www-data WorkingDirectory=/opt/myapp ExecStart=/usr/bin/python3 /opt/myapp/app.py Restart=on-failure RestartSec=5
[Install] WantedBy=multi-user.target
The three sections do distinct jobs. [Unit] holds metadata and ordering (After=network.target waits for networking). [Service] defines how to run the process: Type=simple means the process started by ExecStart is the service itself; Restart=on-failure auto-restarts it after a non-zero exit. [Install] defines what happens on enableโWantedBy=multi-user.target ties it to normal multi-user boot.
Loading and managing your unit
Whenever you add or edit a unit file, reload the systemd manager so it re-reads the files on disk:
sudo systemctl daemon-reloadNow start and enable your service:
sudo systemctl enable --now myapp.serviceConfirm it came up:
systemctl status myapp.serviceReading logs with journalctl
systemd captures the stdout and stderr of every service into the journal. To read a specific service's logs:
journalctl -u myapp.serviceThe most useful flags in daily operations:
journalctl -u myapp.service -f # follow live, like tail -f
journalctl -u myapp.service -n 50 # last 50 lines
journalctl -u myapp.service --since '10 min ago'
journalctl -u myapp.service -p err # only error priority and worseTo see logs from the current boot only:
journalctl -u myapp.service -bVerification
Prove the auto-restart works. Find the main PID, kill it, then check the service recovers:
systemctl show -p MainPID myapp.service
sudo kill <PID>
systemctl status myapp.serviceWithin five seconds (RestartSec=5) systemd should restart the process with a new PID, and the journal will record the restart. That confirms both your unit file and the restart policy are correct.
Common pitfalls and troubleshooting
- Forgetting daemon-reload. After editing a unit file, systemd keeps the old version cached until you run
sudo systemctl daemon-reload. If your changes seem ignored, this is usually why. - Confusing enabled with active.
enableonly affects boot; it does not start the service now. Use--nowto do both. - Wrong Type=. If your program forks into the background,
Type=simplemakes systemd think it exited. UseType=forking(with a PIDFile) or, better, run the process in the foreground. - Service fails immediately. Run
systemctl statusandjournalctl -xeu myapp.serviceโthe-xadds explanatory hints. Common causes are a wrongExecStartpath or aUser=that lacks permissions onWorkingDirectory. - Editing packaged units directly. Do not edit files under
/usr/lib/systemd/system/; package updates overwrite them. Usesudo systemctl edit servicenameto create a drop-in override instead.
Conclusion
With start, stop, enable, status, a custom unit file, and journalctl, you can manage the full lifecycle of any service on an Ubuntu server. These same commands work identically across every systemd-based distribution, so the skill transfers everywhere.
Once your services are defined as units, the next step is scheduling recurring workโcompare cron and systemd timers in our guide to scheduling jobs with cron on Linux, and put a service behind the internet safely with an Nginx reverse proxy. Keeping dozens of services healthy across a fleet is exactly what a managed platform automates: clouditiv watches unit health, restarts, and resource limits for you as part of its monitoring, so you spend less time reading journalctl at 3 a.m.