Every service running on a modern Linux server, from Nginx to PostgreSQL to your custom application, is managed by systemd. The tool you interact with is systemctl, and knowing it well is the difference between confidently managing a production system and fumbling through tab completion hoping for the best.
This reference covers every systemctl operation you will actually use: starting and stopping services, checking status, inspecting unit files, managing boot targets, creating custom services, analyzing boot performance, and more. All commands were tested on Rocky Linux 10 with systemd 257 and apply equally to Ubuntu 24.04, Debian 13, AlmaLinux 10, and Fedora 42.
Verified working: March 2026 on Rocky Linux 10.1 (kernel 6.12), systemd 257, SELinux enforcing
Quick Reference Table
Bookmark this table. These are the commands you will reach for most often.
| Action | Command |
|---|---|
| Start a service | systemctl start nginx |
| Stop a service | systemctl stop nginx |
| Restart a service | systemctl restart nginx |
| Reload config (no downtime) | systemctl reload nginx |
| Enable at boot | systemctl enable nginx |
| Enable and start now | systemctl enable --now nginx |
| Disable at boot | systemctl disable nginx |
| Check status | systemctl status nginx |
| Check if running | systemctl is-active nginx |
| Check if enabled | systemctl is-enabled nginx |
| List running services | systemctl list-units --type=service |
| List failed services | systemctl --failed |
| View unit file | systemctl cat nginx |
| View service logs | journalctl -u nginx |
| Prevent service from starting | systemctl mask nginx |
| Reload all unit files | systemctl daemon-reload |
Start, Stop, Restart, and Reload Services
These four operations are the bread and butter of service management. Start brings a service up, stop shuts it down, restart does a full stop/start cycle, and reload tells the service to re-read its configuration without dropping connections.
Start Nginx:
sudo systemctl start nginx
Stop it:
sudo systemctl stop nginx
Restart performs a full stop then start. Use this after binary upgrades or major config changes:
sudo systemctl restart nginx
Reload tells the service to re-read its configuration without stopping. Active connections stay alive. Not all services support reload, but Nginx, Apache, and most database servers do:
sudo systemctl reload nginx
When you are not sure whether a service supports reload, use reload-or-restart. It tries reload first and falls back to restart:
sudo systemctl reload-or-restart nginx
There is also try-restart, which only restarts the service if it is already running. Useful in scripts where you do not want to accidentally start a stopped service:
sudo systemctl try-restart nginx
Enable and Disable Services at Boot
Starting a service does not make it survive a reboot. You need to enable it separately. What enable actually does is create a symlink in the target directory so systemd knows to start the service during boot.
Enable Nginx to start on boot:
sudo systemctl enable nginx
The output confirms the symlink creation:
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
The most practical variant combines both operations. Enable and start in one shot:
sudo systemctl enable --now nginx
Disable removes the symlink so the service no longer starts at boot. It does not stop a currently running service:
sudo systemctl disable nginx
To disable and stop simultaneously:
sudo systemctl disable --now nginx
Check Service Status
The status command is the single most useful systemctl subcommand. It shows whether the service is running, its PID, memory usage, CPU time, and the most recent log entries all in one view.
sudo systemctl status nginx
On Rocky Linux 10 with Nginx running:
● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: disabled)
Active: active (running) since Tue 2026-03-24 23:07:50 UTC; 5min ago
Main PID: 4857 (nginx)
Tasks: 3 (limit: 22910)
Memory: 3.3M (peak: 6M)
CPU: 75ms
CGroup: /system.slice/nginx.service
├─4857 "nginx: master process /usr/sbin/nginx"
├─6086 "nginx: worker process"
└─6087 "nginx: worker process"
The Loaded line tells you where the unit file lives and whether it is enabled. The Active line shows the current state and uptime. The CGroup tree shows every process belonging to the service, which is extremely useful when a service spawns child processes you did not expect.
Quick status checks for scripts
For scripting, use the one-word check commands. They return a clean string and set the exit code:
systemctl is-active nginx
Returns active or inactive. Check if enabled at boot:
systemctl is-enabled nginx
Check if a service has failed:
systemctl is-failed nginx
These are perfect for conditionals. The --quiet flag suppresses output and only sets the exit code:
if systemctl is-active --quiet nginx; then
echo "Nginx is running"
fi
Check overall system health:
systemctl is-system-running
Returns running if everything is healthy, degraded if any unit failed, or initializing if the system is still booting. When it says degraded, check which unit caused it:
systemctl --failed
List Running Services and Units
See all active services on the system:
systemctl list-units --type=service --state=running
On a minimal Rocky Linux 10 server:
UNIT LOAD ACTIVE SUB DESCRIPTION
auditd.service loaded active running Security Audit Logging Service
chronyd.service loaded active running NTP client/server
crond.service loaded active running Command Scheduler
dbus-broker.service loaded active running D-Bus System Message Bus
NetworkManager.service loaded active running Network Manager
nginx.service loaded active running The nginx HTTP and reverse proxy server
rsyslog.service loaded active running System Logging Service
sshd.service loaded active running OpenSSH server daemon
systemd-journald.service loaded active running Journal Service
systemd-udevd.service loaded active running Rule-based Manager for Device Events and Files
21 loaded units listed.
Filter by other states:
systemctl list-units --type=service --state=inactive
systemctl list-units --type=service --state=failed
Show all units including sockets, mounts, and timers:
systemctl list-units --all
To see what is installed regardless of whether it is running, use list-unit-files. The STATE column shows enabled, disabled, static (cannot be enabled directly), or masked:
systemctl list-unit-files --type=service
Sample output from Rocky Linux 10:
UNIT FILE STATE PRESET
auditd.service enabled enabled
chronyd.service enabled enabled
crond.service enabled enabled
dbus-broker.service enabled enabled
httpd.service disabled disabled
NetworkManager.service enabled enabled
nginx.service enabled disabled
sshd.service enabled enabled
The PRESET column shows the vendor default. When STATE differs from PRESET (like nginx above), someone manually changed it. Filter by specific state:
systemctl list-unit-files --type=service --state=enabled
Inspect Unit Files and Properties
Every service is defined by a unit file. To read the full unit file without hunting for it on disk:
systemctl cat nginx.service
This prints the file with a comment showing the path:
# /usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=true
[Install]
WantedBy=multi-user.target
For specific properties, use show with the --property flag. This is far more useful than parsing status output:
systemctl show nginx.service --property=MainPID,ActiveState,SubState,MemoryCurrent
Returns clean key=value pairs, perfect for scripting:
MainPID=4857
ActiveState=active
SubState=running
MemoryCurrent=3530752
To see the full dependency tree of a service:
systemctl list-dependencies nginx.service
This reveals what the service requires and what it waits for before starting:
nginx.service
● ├─-.mount
● ├─system.slice
● ├─network-online.target
● │ └─NetworkManager-wait-online.service
● └─sysinit.target
● ├─dev-hugepages.mount
● ├─systemd-journald.service
● ├─systemd-tmpfiles-setup.service
● └─local-fs.target
Show reverse dependencies (what depends on this service):
systemctl list-dependencies nginx.service --reverse
View Service Logs with journalctl
Systemd captures all service output (stdout and stderr) in the journal. You do not need to know where a service writes its log file because journalctl has it all. For a deeper look at filtering options, see our guide on filtering systemd logs with journalctl.
Show recent logs for a service:
sudo journalctl -u nginx.service -n 20
The -n 20 flag shows the last 20 entries. Without it, you get everything since the last boot, which can be thousands of lines.
Follow logs in real time (like tail -f):
sudo journalctl -u nginx.service -f
Filter by time window:
sudo journalctl -u nginx.service --since "1 hour ago"
sudo journalctl -u nginx.service --since "2026-03-24 22:00" --until "2026-03-24 23:00"
Show only errors and above (critical, alert, emergency):
sudo journalctl -u nginx.service -p err
Priority levels go from 0 (emerg) to 7 (debug). Common filters: -p warning, -p err, -p crit.
Show logs from the current boot only:
sudo journalctl -u nginx.service -b
If your journal does not persist across reboots, configure persistent systemd journal storage so you can review logs from previous boots.
Mask and Unmask Services
Masking is stronger than disabling. A disabled service can still be started manually or pulled in as a dependency. A masked service cannot be started at all, by anyone or anything, until it is unmasked. This is how you prevent a service from running under any circumstances.
Mask a service:
sudo systemctl mask httpd.service
Systemd creates a symlink pointing to /dev/null:
Created symlink '/etc/systemd/system/httpd.service' → '/dev/null'.
Any attempt to start the masked service fails immediately:
sudo systemctl start httpd.service
The error is clear:
Failed to start httpd.service: Unit httpd.service is masked.
Unmask restores the original state:
sudo systemctl unmask httpd.service
A common use case: you have both Nginx and Apache installed but only want Nginx running. Mask Apache so nothing accidentally starts it.
Create a Custom Systemd Service
Any long-running process can become a systemd service. This gives you automatic restarts, logging via journalctl, and proper dependency ordering. For more on this topic, including running services without root privileges, see our dedicated guide.
Create the unit file:
sudo vi /etc/systemd/system/myapp.service
Add the following configuration:
[Unit]
Description=My Custom Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 -m http.server 9090
Restart=on-failure
RestartSec=5
User=nobody
Group=nobody
[Install]
WantedBy=multi-user.target
The key directives are:
- After – Wait for the network before starting
- Type=simple – The process started by ExecStart is the main service process
- Restart=on-failure – Automatically restart if the process exits with a non-zero code
- RestartSec=5 – Wait 5 seconds before restarting
- User/Group – Run as an unprivileged user
Reload the unit file database and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
Verify it is running:
sudo systemctl status myapp.service
You should see active (running) with the PID and memory usage. You can also run containers as systemd services, which is covered in our guide on running Docker and Podman containers as systemd services.
Common service types
| Type | When to use | Example |
|---|---|---|
simple | Process runs in foreground | Python scripts, Node.js apps |
forking | Process forks and parent exits | Nginx, Apache |
oneshot | Process runs once and exits | Backup scripts, cleanup tasks |
notify | Process signals readiness via sd_notify | PostgreSQL, systemd-aware apps |
dbus | Process acquires a D-Bus name | NetworkManager, PulseAudio |
Override Unit File Settings
Never edit vendor unit files in /usr/lib/systemd/system/ directly. Package updates will overwrite your changes. Instead, create a drop-in override that only replaces the specific directives you need to change.
The clean way is with systemctl edit, which creates the override directory and file for you:
sudo systemctl edit nginx.service
This opens an editor. Add the directives you want to override. For example, to increase the open file limit:
[Service]
LimitNOFILE=65536
Save and exit. Systemd creates /etc/systemd/system/nginx.service.d/override.conf and automatically runs daemon-reload. Verify the override took effect:
systemctl show nginx.service --property=LimitNOFILE
Confirms the new value:
LimitNOFILE=65536
To edit the full unit file instead of creating an override (replaces the entire file):
sudo systemctl edit --full nginx.service
To remove all overrides and revert a service to its original vendor configuration:
sudo systemctl revert nginx.service
This deletes the override directory and any full copies in /etc/systemd/system/. The service reverts to the vendor unit file.
If you create or modify unit files manually (without systemctl edit), always reload the daemon afterward:
sudo systemctl daemon-reload
Manage Systemd Timers
Systemd timers are the modern replacement for cron jobs. They offer better logging (via journalctl), dependency ordering, and the ability to catch up on missed runs. For a complete walkthrough, see our guide on configuring cron jobs with systemd timers.
List all active timers on the system:
systemctl list-timers
On Rocky Linux 10, the default installation includes several timers:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Tue 2026-03-24 23:21:28 UTC 8min - - systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Wed 2026-03-25 00:08:33 UTC 55min - - dnf-makecache.timer dnf-makecache.service
Wed 2026-03-25 00:18:19 UTC 1h 5min - - logrotate.timer logrotate.service
Wed 2026-03-25 00:20:45 UTC 1h 7min - - fwupd-refresh.timer fwupd-refresh.service
Sun 2026-03-29 01:00:00 UTC 4 days - - raid-check.timer raid-check.service
Mon 2026-03-30 00:33:03 UTC 5 days - - fstrim.timer fstrim.service
6 timers listed.
The NEXT column tells you exactly when each timer fires next. The ACTIVATES column shows which service it triggers.
Include inactive timers too:
systemctl list-timers --all
Check the status of a specific timer:
systemctl status logrotate.timer
Enable or disable timers the same way as services:
sudo systemctl enable --now fstrim.timer
sudo systemctl disable --now fstrim.timer
Systemd Targets (Runlevels)
Systemd targets replace the old SysVinit runlevels. A target groups related units together and defines a system state. The two most common targets on servers are multi-user.target (text mode, no GUI) and graphical.target (with a desktop environment).
Check the current default target:
systemctl get-default
On a server, this returns:
multi-user.target
Change the default target (takes effect on next boot):
sudo systemctl set-default graphical.target
sudo systemctl set-default multi-user.target
Switch to a target immediately without rebooting:
sudo systemctl isolate multi-user.target
The isolate command stops all units not required by the target, so be careful with this on production servers.
Here is how the old runlevels map to systemd targets:
| Runlevel | Systemd Target | Purpose |
|---|---|---|
| 0 | poweroff.target | Halt the system |
| 1 | rescue.target | Single-user mode |
| 3 | multi-user.target | Multi-user, no GUI |
| 5 | graphical.target | Multi-user with GUI |
| 6 | reboot.target | Reboot |
List all available targets on the system:
systemctl list-unit-files --type=target
Analyze Boot Performance
When a server takes too long to boot, systemd-analyze tells you exactly which service is to blame.
Overall boot time breakdown:
systemd-analyze
On the test Rocky 10 VM:
Startup finished in 1.104s (kernel) + 2.452s (initrd) + 27.021s (userspace) = 30.578s
multi-user.target reached after 9.395s in userspace.
The total boot was 30 seconds, but the system was usable (multi-user.target) after 9.4 seconds. The remaining time was background services like kdump.
Find the slowest services with blame:
systemd-analyze blame
Output sorted from slowest to fastest:
17.754s kdump.service
5.331s cloud-init-local.service
1.244s cloud-init.service
201ms ldconfig.service
186ms systemd-tmpfiles-setup.service
151ms NetworkManager-wait-online.service
98ms rsyslog.service
64ms NetworkManager.service
53ms cockpit.socket
In this case, kdump.service at 17 seconds is the biggest offender. On a production server where kdump is not needed, disabling it would cut boot time in half.
See the critical chain (the sequence of units that determined overall boot time):
systemd-analyze critical-chain
This shows the longest dependency path:
multi-user.target @9.395s
└─rsyslog.service @9.288s +98ms
└─network-online.target @9.255s
└─cloud-init.service @8.007s +1.244s
└─NetworkManager-wait-online.service @7.849s +151ms
└─NetworkManager.service @7.778s +64ms
└─network-pre.target @7.776s
└─cloud-init-local.service @2.443s +5.331s
└─basic.target @2.436s
Read it bottom to top. Each unit had to finish before the next one could start. The @ timestamp shows when a unit activated, and the + value shows how long it took.
Kill Service Processes
Sometimes you need to send a specific signal to a service without doing a full restart. The systemctl kill command sends a signal to all processes in the service’s cgroup.
Send SIGHUP to reload Nginx configuration (equivalent to nginx -s reload):
sudo systemctl kill nginx.service --signal=SIGHUP
Send SIGUSR1 to trigger log rotation (some services support this):
sudo systemctl kill nginx.service --signal=SIGUSR1
By default, kill sends the signal to all processes in the cgroup. To target only the main process:
sudo systemctl kill nginx.service --signal=SIGTERM --kill-whom=main
The difference between systemctl kill and systemctl stop is that kill sends a raw signal while stop follows the unit file’s ExecStop sequence, which may include graceful shutdown steps.
System Power Management
Systemctl also handles system power state transitions. These commands replace the old shutdown, halt, and reboot commands (which still work as symlinks).
Reboot the system:
sudo systemctl reboot
Power off:
sudo systemctl poweroff
Halt (stops all services but does not power off the hardware):
sudo systemctl halt
Enter rescue mode (single-user mode, for emergency maintenance):
sudo systemctl rescue
Suspend to RAM (laptop/desktop, rarely used on servers):
sudo systemctl suspend
Hibernate to disk:
sudo systemctl hibernate
Differences Between RHEL and Debian Families
The systemctl commands themselves are identical across distributions. The differences are in service names, package names, and file paths.
| Item | RHEL/Rocky/AlmaLinux | Ubuntu/Debian |
|---|---|---|
| Web server package | httpd | apache2 |
| Web server service | httpd.service | apache2.service |
| Firewall service | firewalld.service | ufw.service |
| NTP service | chronyd.service | systemd-timesyncd.service |
| Unit file path (vendor) | /usr/lib/systemd/system/ | /lib/systemd/system/ |
| Package manager timer | dnf-makecache.timer | apt-daily.timer |
| SELinux/AppArmor | SELinux enforcing | AppArmor enabled |
The systemctl syntax does not change. Only the service name after the command differs.
Going Further
This covers the core systemctl operations. For specific use cases, these resources go deeper:
- The official systemctl man page documents every flag and subcommand
- Use
systemctl editto customize resource limits (CPU, memory, IO) per service via cgroup controls - Explore socket activation with
systemctl list-socketsfor on-demand service startup - Use
systemd-cglsandsystemd-cgtopfor real-time cgroup resource monitoring - For scheduling tasks, systemd timers offer better logging and dependency handling than cron