Manage systemd services

Use systemctl to start, stop, enable, disable, and inspect services on Fedora Linux.

When a service refuses to start

You rebooted your Fedora machine after a routine dnf upgrade and the desktop never loads. The screen sits at a blinking cursor or drops you to a login prompt that refuses to connect to the network. You suspect a background process failed, but you are not sure which one or how to fix it without guessing.

What systemd actually does

systemd is the init system and service manager on Fedora. It does not just launch programs. It tracks dependencies, applies resource limits, and restarts processes when they crash. Think of it like a theater stage manager. The script defines which actors go on stage and in what order. The stage manager watches the cues, restarts a scene if an actor forgets a line, and ensures the lights come on before the curtain rises. When a service fails, systemd logs exactly why the cue was missed and what state the system is currently in.

The primary interface is systemctl. Every background process, mount point, timer, and socket is represented as a unit. You interact with units by name. The name usually matches the package or daemon, followed by .service. If you omit the suffix, systemd assumes .service by default. Units declare what they need to run using Requires= and Wants= directives. They declare when they should run using After= and Before=. systemd builds a dependency graph and launches everything in the correct order.

Run systemctl status first. Read the actual error before guessing.

Checking state and reading logs

You need to know whether a unit is running, failed, or masked before you try to fix it. The status command shows the current state, the main process ID, and the last few log lines.

systemctl status httpd
# Shows active state, PID, and recent journal entries for the unit
# The green "active (running)" line means the daemon is healthy
# A red "failed" line means systemd gave up after hitting a restart limit

If the output says failed, the logs hold the answer. journalctl is the system log viewer. Most administrators type journalctl -xeu <unit> by muscle memory. The x flag adds explanatory hints to cryptic log lines. The e flag jumps to the end of the journal. The u flag filters for a specific unit.

journalctl -xeu httpd
# -x adds explanatory text to priority log lines
# -e jumps to the end of the journal so you see recent failures
# -u filters output to only the httpd unit

If you see a line like Failed at step EXEC spawning /usr/sbin/httpd: No such file or directory, the binary path in the unit file is wrong or the package was partially removed. If you see Permission denied, SELinux or file permissions are blocking the daemon. Check /var/log/audit/audit.log or run journalctl -t setroubleshoot to read the one-line SELinux summary before disabling security policies.

Reboot before you debug. Half the time the symptom is gone.

Starting, stopping, and restarting

You control the runtime state with start, stop, and restart. These commands affect the current session only. They do not change what happens on the next boot.

systemctl start httpd
# Launches the daemon immediately without changing boot persistence
systemctl stop httpd
# Sends SIGTERM to the main process and waits for graceful shutdown
systemctl restart httpd
# Stops the service and starts it again in a single atomic operation

Use restart when you changed a configuration file and need the daemon to read it. Use reload when the daemon supports hot-reloading configuration without dropping active connections. If you are unsure, restart is safer. It guarantees a clean state.

Always check systemctl status <unit> before you restart. A service that is already dead will not magically fix itself, and restarting it will just trigger the same failure loop.

Making changes survive a reboot

Runtime commands vanish when the machine powers off. To make a service start automatically, you enable it. Enabling creates a symbolic link in the systemd preset directories so the init system knows to launch the unit during boot.

systemctl enable httpd
# Creates symlinks in /etc/systemd/system/ so the unit starts at boot
systemctl disable httpd
# Removes the symlinks and prevents automatic startup
systemctl enable --now httpd
# Enables the unit for boot and starts it immediately in one step

The --now flag saves a terminal round-trip. It is the standard way to install a new service and put it to work immediately. If you need to check whether a unit is enabled without starting it, run systemctl is-enabled httpd. It prints enabled, disabled, static, or masked.

Trust the package manager. Manual file edits drift, snapshots stay.

Editing unit files and overrides

Unit files define how systemd manages a service. They specify the executable path, environment variables, resource limits, and dependency order. System-provided units ship in /usr/lib/systemd/system/. Local overrides and custom units go in /etc/systemd/system/. Files in /etc/ always take precedence. Never edit files in /usr/lib/. Package updates will overwrite them and your changes will disappear.

To modify an existing service, use systemctl edit. This opens a drop-in override file in your editor.

systemctl edit httpd
# Opens /etc/systemd/system/httpd.service.d/override.conf
# Add or override directives here instead of touching the original file
# Example: [Service] section followed by Environment="VAR=value"

After creating or editing any unit file, you must tell systemd to re-read its configuration. The daemon caches unit states in memory. Without a reload, your changes sit on disk and do nothing.

systemctl daemon-reload
# Forces systemd to rescan all unit files and update its internal state
# Run this after every unit file creation, deletion, or modification

If you accidentally break a unit file, systemd will refuse to start it and print a parse error. Check the syntax with systemd-analyze verify httpd.service before reloading. It catches missing sections and invalid directives.

Switching runlevels with targets

Targets group units together to represent a system state. They replace the old SysV runlevels. multi-user.target is the standard text-mode state. graphical.target pulls in the display manager and desktop environment. rescue.target drops you to a single-user root shell for recovery.

systemctl get-default
# Prints the target that boots automatically
systemctl set-default multi-user.target
# Updates the symlinks so the system boots to text mode next time
systemctl isolate graphical.target
# Switches to the target immediately without rebooting

Use set-default when you want to change the permanent boot behavior. Use isolate when you need to switch states on a running system. Isolating a target stops units that are not part of the new target and starts the ones that are. It is faster than rebooting but can drop active network connections.

If the boot menu is gone, GRUB rescue is your friend, not your enemy.

Managing containers as native services

Podman ships with Quadlet, a tool that translates container definitions into systemd unit files. You place a .container file in /etc/containers/systemd/ and systemd handles the lifecycle automatically. The container starts on boot, restarts on crash, and logs to the journal.

sudo cp myapp.container /etc/containers/systemd/
# Places the Quadlet definition where systemd can discover it
sudo systemctl daemon-reload
# Picks up the new .container file and generates the .service unit
sudo systemctl enable --now myapp.service
# Starts the container and registers it for automatic boot startup

Quadlet units behave like any other systemd service. You use systemctl status, journalctl -u, and systemctl restart exactly as described above. The only difference is the underlying process is a container runtime instead of a native daemon.

Common pitfalls and what the error looks like

Services fail for predictable reasons. Recognizing the pattern saves hours of trial and error.

The systemctl start command will refuse to proceed and print the following error when a unit is intentionally disabled by the system or a package maintainer:

Failed to start httpd.service: Unit httpd.service is masked.

Masked units are symlinked to /dev/null. Run systemctl unmask httpd to restore the original unit file before enabling it.

If you see [FAILED] Failed to start NetworkManager.service during boot, your network configuration probably references a missing interface name or a stale MAC address. Check /etc/NetworkManager/conf.d/ for custom device settings.

Another frequent issue is the restart loop. When a service crashes repeatedly, systemd enforces a StartLimitIntervalSec policy. You will see Unit entered failed state followed by Failed with result 'exit-code'. The service stops trying to restart to prevent CPU thrashing. Run systemctl reset-failed httpd to clear the failure counter, then check the logs again.

SELinux denials are the silent killer. A service will fail to bind a port or read a configuration file, and the journal will show a generic permission error. Run journalctl -t setroubleshoot to read the one-line summary. It tells you exactly which file context is wrong and how to fix it with semanage fcontext and restorecon.

Run journalctl first. Read the actual error before guessing.

When to use each approach

Use systemctl status when you need to verify the current state and recent logs in a single view. Use journalctl -xeu when the status output is too brief and you need the full diagnostic trail. Use systemctl start when you need a temporary runtime instance that should not survive a reboot. Use systemctl enable --now when you are installing a new service and want it running immediately and persistently. Use systemctl edit when you need to override package defaults without touching /usr/lib/. Use systemctl isolate when you need to switch system states on a live machine without rebooting. Use systemctl daemon-reload after every unit file change, not before. Use Quadlet when you want containers managed with the same lifecycle guarantees as native daemons.

Snapshot the system before the upgrade. Future-you will thank you.

Where to go next