Most state-changing commands need ; reading status and listing units does not. Unit names can drop the suffix ( and are the same).
Manage a Service
| Command | What it does |
|---|---|
| Running or not, PID, memory, and the last ten log lines | |
| Same, full lines, no pager (for scripts and copy-paste) | |
| Start it now | |
| Stop it now | |
| Stop then start; drops connections | |
| Reload config without stopping; only for services that support it | |
| Reload if it can, otherwise restart | |
| Restart only if it is already running | |
| Prints or ; exit code 0 when running, for scripts | |
| Prints and exits 0 if it has failed | |
| Send SIGTERM to every process in the unit | |
| Send a different signal | |
| One property, here the main process ID | |
| Several properties | |
| Clear the state (and the start rate limit) | |
| Several units in one command |
and differ: sends the service its reload signal (nginx re-reads its config with no dropped connections), kills and relaunches it. exits non-zero when the service is not running, which trips up scripts that only wanted the output.
Enable and Disable
Enabling controls what happens at boot. It does not start anything on its own, and starting does not enable anything.
| Command | What it does |
|---|---|
| Start at boot (creates the symlink under ) | |
| Start at boot and start it now | |
| Do not start at boot | |
| Remove from boot and stop it now | |
| , , , , or | |
| Make it impossible to start, even by another unit (links it to ) | |
| Undo that | |
| Disable then enable; useful after the section changed | |
| Enable or disable according to the distro's preset policy |
means the unit has no section and is pulled in by something else; you cannot enable it and do not need to. means someone masked it, on purpose or by a package; restores it.
List Units
| Command | What it does |
|---|---|
| Every loaded, active unit (services, sockets, mounts, timers) | |
| Loaded services and their state | |
| Only running services | |
| Include inactive and not-found services | |
| Units that failed | |
| Same thing, shorter | |
| Every installed service file and whether it is enabled | |
| Only enabled ones | |
| Active targets (the systemd version of runlevels) | |
| Socket units and the service each one activates | |
| What nginx pulls in | |
| What depends on nginx | |
| Jobs in progress (usually empty, useful when boot hangs) | |
| Grep the output | |
| , (something failed), , or |
shows what systemd has loaded; shows what is installed on disk. A service that is installed but has never started shows up only in the second.
Logs
systemd services log to the journal, and reads it. These are the calls you need alongside systemctl; the journalctl cheat sheet covers the full toolkit.
| Command | What it does |
|---|---|
| Every log line from the nginx unit | |
| Follow live, like | |
| Last 100 lines | |
| Same, straight to the terminal | |
| Time window ( also works) | |
| Since midnight | |
| Absolute time | |
| Since the current boot | |
| From the previous boot | |
| Priority and worse (, , , ) | |
| ISO timestamps ( for structured output) | |
| Several units interleaved | |
| Recent log with explanations, what tells you to run | |
| Kernel messages only ( equivalent) | |
| How much space the journal takes | |
| Trim it to 500 MB ( by age) | |
| Boot IDs for |
shows the last ten lines. When the service failed to start and those lines do not explain why, usually does. If shows nothing for a unit, the service may be writing to its own log file under instead of stdout.
Failed Services
| Command | What it does |
|---|---|
| Everything that failed, the first thing to run on a sick box | |
| means at least one unit failed | |
| The failure with systemd's own explanations, what points you to | |
| Only errors from this boot | |
| Why it stopped: , , , | |
| The process exit code and how it died | |
| How many times it has been restarted | |
| Clear the failed state and the start rate limit | |
| Clear it for every unit | |
| Catch syntax and path errors without loading it | |
| Run the command as a throwaway unit to reproduce the failure |
The code on the failure line narrows it down before you read a single log line:
| Code | What went wrong |
|---|---|
| is not an absolute path, does not exist, or is not executable | |
| does not exist or the user cannot enter it | |
| The account does not exist on this machine | |
| A sandboxing option (, ) points somewhere invalid | |
| The file is there but has no execute bit | |
| A shell ran the command and could not find it, usually a bad | |
| The program itself exited non-zero; the reason is in its own log output |
means either a typo in the name or a new file that systemd has not read yet, so run first. is the rate limit rather than the real error: read the journal for the original crash, fix it, then before starting again.
Timers
Timers are systemd's cron: a unit that starts a matching on a schedule.
| Command | What it does |
|---|---|
| Active timers with next and last run times | |
| Include inactive ones | |
| Enable and start a timer (enable the timer, not the service) | |
| Run the job now, outside the schedule | |
| When it fires next | |
| What the last runs logged | |
| Test an expression and see the next run | |
| The next five runs |
A timer that runs a script every night at 03:00, in two files:
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup nightly
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.targetruns a missed job at the next boot, which cron never did. Have a crontab line already? The cron to systemd timer converter turns it into these two files, and the cron generator helps with the schedule itself.
Edit Unit Files
Your units go in . Package units live in (or on Debian and Ubuntu, which is the same directory) and get overwritten on upgrade, so never edit those directly. A file in with the same name wins over the one in .
| Command | What it does |
|---|---|
| Print the unit file and every drop-in, with their paths | |
| Create or edit a drop-in override in | |
| Copy the whole unit to and edit that | |
| Create a brand new unit from scratch in your editor | |
| Delete your overrides and go back to the package's unit | |
| Re-read every unit file; required after editing one by hand | |
| Every property with its effective value | |
| What will actually run | |
| Where the unit and its overrides live | |
| Check a unit file for errors before loading it | |
| Sandboxing score and which hardening options are missing |
opens an empty drop-in in ; whatever you type is layered on top of the original, and it runs for you. It does not restart the service, so follow with . Overriding in a drop-in needs an empty line first, because lines add up:
[Service]
ExecStart=
ExecStart=/usr/sbin/nginx -c /etc/nginx/custom.confA minimal service unit for your own program:
[Unit]
Description=My app
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=app
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/server --port 3000
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.targetSave it as , then and . The systemd unit generator writes this for you and the systemd unit validator checks one you already have. Moving a Node app off pm2? The pm2 to systemd converter turns an ecosystem file into units.
Remove a Service
| Command | What it does |
|---|---|
| Find every file before deleting anything | |
| Stop it and take it out of boot | |
| Delete your own unit | |
| Delete its drop-in overrides too | |
| Make systemd forget it | |
| Clear the leftover entry from | |
| Keep a packaged unit from ever starting, without deleting it |
Only delete files under . A unit that came from a package lives in , so deleting it there gets undone by the next upgrade; it, or it if something else keeps pulling it in, and remove the package if you want it gone for good. Skipping leaves a ghost entry that still shows up as failed after the file is gone.
Dependencies and Order
| Command | What it does |
|---|---|
| The tree of units nginx pulls in | |
| What would break if nginx did not start | |
| Units that must start before nginx (its chain) | |
| Units that wait for nginx to start first | |
| Everything the normal boot target starts | |
| The directives in effect, after drop-ins are merged | |
| Which units asked for this one | |
| The chain that gated nginx starting, with timings |
Ordering and requirement are separate: only says when, and say what has to be pulled in, and a unit with but no can start before its dependency is ready. tolerates the other unit failing, does not, and also stops your unit when the other one goes away. Use with a matching when a service needs a routable address, since only means the stack has been set up.
Boot and Power
| Command | What it does |
|---|---|
| Reboot (same as ) | |
| Shut down and power off (same as , ) | |
| Stop the system without powering off | |
| Sleep to RAM | |
| Sleep to disk (needs swap at least the size of RAM) | |
| Both | |
| The target the system boots into ( or ) | |
| Boot to a console, no display manager | |
| Boot to a login screen | |
| Switch to that target now (stops the display manager) | |
| Single-user rescue mode | |
| Emergency shell, root filesystem read-only | |
| How long the last boot took, kernel vs userspace | |
| Every unit sorted by how long it took to start | |
| The chain of units that gated boot | |
| Boot timeline as an SVG |
shows time spent, not time that delayed the boot; a unit can take 30 seconds in parallel without slowing anything. shows the ones that actually held things up.
User Services
manages units under your own account, without , from . Handy for a personal sync client, an editor daemon, or a dev server.
| Command | What it does |
|---|---|
| Status of a user unit | |
| Enable and start it | |
| Your user services | |
| After editing a file in | |
| Drop-in override for a user unit | |
| Its logs | |
| Keep user services running when you log out, and start them at boot |
Without , user services stop when your last session closes, which is the usual reason a service dies as soon as you disconnect from SSH.
service and chkconfig
The old SysV commands still exist on most distros and forward to systemd, so they work but hide what is happening and drop the useful output.
| Old command | systemctl equivalent |
|---|---|
| for the boot target, for the current one | |
is not , which reads and writes kernel parameters and has nothing to do with services.
Gotchas
- is now, is at boot. A service you enabled but never started is still stopped until you reboot; does both.
- After editing any unit file by hand, run , or systemd keeps using the old version and warns . reloads for you but does not restart.
- A drop-in that sets needs on its own line first to clear the original, or the unit fails with .
- is the start rate limit, not the real error. Read for the crash that caused it, fix that, then before starting again.
- means someone ran , or the package masks it by design. first, then .
- truncates long lines and pages its output. Add when you want to copy from it or pipe it.
- means no systemd: most Docker containers, macOS, Alpine, and WSL without in . Inside a container, run the process directly and let the container's restart policy handle failures; the docker commands sheet covers that side.
- needs an absolute path and does not understand shell syntax. fails; use and , or call explicitly.