systemd is the init system and service manager used by many modern Linux distributions. It is responsible for booting the system, starting services, tracking their state, and managing logs and dependencies.
If you run a server with Nginx, Docker, SSH, or a custom app, systemd often controls whether that service starts at boot and how it behaves.
systemd manages objects called units. A unit describes something systemd knows how to manage.
Common unit types include:
.service for services.socket for socket activation.mount for mount points.timer for scheduled tasks.target for grouped system statesA target is roughly like a collection of units that represents a system state, such as multi-user mode.
systemctlsystemctl is the main command for working with systemd.
Start a service:
sudo systemctl start nginxStop a service:
sudo systemctl stop nginxRestart a service:
sudo systemctl restart nginxCheck status:
systemctl status nginxThis shows whether the service is active, recent log lines, and the main process ID.
A service can be running now without being configured to start at boot.
Enable at boot:
sudo systemctl enable nginxDisable at boot:
sudo systemctl disable nginxA common beginner mistake is assuming start and enable mean the same thing. They do not.
start affects the current sessionenable affects future bootsTip: Many deployment issues happen because a service was started manually but never enabled.
journalctlsystemd integrates with the system journal, which you can query using journalctl.
Show recent logs:
journalctlFollow logs in real time:
journalctl -fView logs for a specific unit:
journalctl -u nginx
journalctl -u sshd -fFilter by time:
journalctl --since "2026-07-14 09:00:00"
journalctl -u nginx --since "1 hour ago"This is incredibly useful for diagnosing why a service failed to start.
You can manage your own applications with systemd by creating a unit file.
Example:
[Unit]
Description=Demo Application Service
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/demo-app
ExecStart=/usr/bin/python3 /opt/demo-app/app.py
Restart=on-failure
User=demo
Group=demo
[Install]
WantedBy=multi-user.targetSave it as:
/etc/systemd/system/demo-app.serviceThen reload systemd and start the service:
sudo systemctl daemon-reload
sudo systemctl start demo-app
sudo systemctl enable demo-app[Unit]This section describes metadata and dependencies.
[Service]This section defines how the service starts, under which user, in which directory, and what should happen if it fails.
[Install]This section describes how the unit is tied into targets when enabled.
Suppose your service will not start after deployment.
A useful workflow is:
systemctl status demo-app
journalctl -u demo-app --since "10 minutes ago"Typical causes include:
ExecStart pathsystemd gives you a repeatable place to inspect that behavior.
Question: What is the difference between systemctl start nginx and systemctl enable nginx?
<MCQExercise exercise={{
id: 'linux-systemd-q1',
title: 'Exercise 1: Start versus enable',
description: 'Identify which action affects the current runtime and which affects future boots.',
options: [
{ id: 'a', text: 'start configures boot behavior, while enable only reloads logs.' },
{ id: 'b', text: 'start affects the current runtime, while enable configures the service to start automatically at boot.' },
{ id: 'c', text: 'Both commands mean exactly the same thing in systemd.' },
{ id: 'd', text: 'enable starts the service now but prevents it from starting at boot.' },
],
correctId: 'b',
explanation: 'The tutorial explicitly warns against mixing these up. start controls the current session, while enable creates the boot-time relationship so the service comes up automatically after future reboots.',
wrongExplanations: {
a: 'That reverses the actual meaning. start is the immediate runtime action, not the boot configuration step.',
c: 'They are closely related but not identical. The lesson specifically distinguishes current state from boot behavior.',
d: 'enable does the opposite of preventing boot startup. It is what makes automatic startup happen.',
},
}} />
Scenario: A custom app service fails after deployment, and you want only its journal entries from the last 10 minutes.
Question: Which command from the lesson is the best match?
<MCQExercise exercise={{
id: 'linux-systemd-q2',
title: 'Exercise 2: Filtering the journal',
description: 'Choose the journalctl command that narrows logs to one unit and a recent time window.',
options: [
{ id: 'a', text: 'journalctl -u demo-app --since "10 minutes ago"' },
{ id: 'b', text: 'ls -l /etc/systemd/system/demo-app.service' },
{ id: 'c', text: 'systemctl enable demo-app' },
{ id: 'd', text: 'pwd demo-app' },
],
correctId: 'a',
explanation: 'The lesson shows journalctl -u for unit filtering and --since for time filtering. Combining them gives a focused troubleshooting view that is much more useful than scanning the entire journal.',
wrongExplanations: {
b: 'Listing the unit file may confirm it exists, but it does not show runtime log entries.',
c: 'Enabling the service changes boot behavior. It does not read logs.',
d: 'pwd only shows the current directory and is unrelated to systemd troubleshooting.',
},
}} />
Question: Why do you run systemctl daemon-reload after creating or editing a unit file in /etc/systemd/system?
<MCQExercise exercise={{
id: 'linux-systemd-q3',
title: 'Exercise 3: Making systemd notice changes',
description: 'Pick the reason daemon-reload is necessary after unit file changes.',
options: [
{ id: 'a', text: 'It forces a full system reboot so unit files can be parsed again.' },
{ id: 'b', text: 'It deletes old journal entries for the unit.' },
{ id: 'c', text: 'It tells systemd to rescan and load the updated unit definitions from disk.' },
{ id: 'd', text: 'It switches the service user to root automatically.' },
],
correctId: 'c',
explanation: 'The tutorial explains that systemd does not automatically reread changed unit files the moment you save them. daemon-reload makes the manager rescan those definitions so later start, restart, or enable actions use the new version.',
wrongExplanations: {
a: 'A reboot is not what daemon-reload does, and the lesson shows it as a normal command before starting the service.',
b: 'Journal cleanup is unrelated to reloading unit definitions.',
d: 'Service identity is controlled in the unit file itself through fields like User and Group, not by daemon-reload.',
},
}} />
start is now, enable is at boot. They are independent, and forgetting enable is the classic mistake.systemctl enable --now <unit> does both in one command.systemctl daemon-reload, or systemd keeps running the old definition.Type= must match how your process behaves. simple for a foreground process, notify for one that signals readiness.journalctl -u <unit> -f is the first command to run when a service misbehaves.systemctl edit <unit> for overrides so a package upgrade does not overwrite your changes.Restart=on-failure plus RestartSec turns a crash into a blip instead of an outage.systemd caches unit definitions, so it keeps using the old one until you run sudo systemctl daemon-reload. After that, systemctl restart <unit> picks up the new configuration. If a change still seems ignored, check you edited the file systemd is actually reading, systemctl cat <unit> prints the effective definition including any drop-in overrides, which is the quickest way to spot that your edit is being overridden by something in /etc/systemd/system/<unit>.d/.
Create /etc/systemd/system/myapp.service with three sections: [Unit] for the description and ordering (After=network.target), [Service] for ExecStart, User, WorkingDirectory and Restart=on-failure, and [Install] with WantedBy=multi-user.target so enable knows where to link it. Then daemon-reload and enable --now. Use an absolute path in ExecStart, systemd does not consult your PATH, and remember the service runs as root unless you set User=.
It tells systemd how to know the service has started. Type=simple assumes readiness the moment the process is forked, which suits anything that stays in the foreground. Type=forking is for daemons that background themselves, and needs PIDFile=. Type=notify waits for the process to signal readiness via sd_notify, which is the most accurate and what dependent units should rely on. Type=oneshot is for a task that runs and exits, often with RemainAfterExit=yes. A mismatch here is why dependencies start too early.
journalctl -u myapp shows that unit's log, -f follows it live, and -n 50 limits it to the last 50 lines. --since "10 minutes ago" narrows by time and -p err by priority. Add -b for the current boot only, or -b -1 for the previous one, which is how you find out why the machine went down. By default the journal may be volatile and lost on reboot. Set Storage=persistent in /etc/systemd/journald.conf if you need history to survive.
sudo systemctl edit <unit> creates a drop-in at /etc/systemd/system/<unit>.d/override.conf containing only your changes, which a package upgrade will not touch. Editing the shipped file under /lib/systemd/system/ risks losing your work on the next update. One quirk worth knowing: to replace a directive that takes a list, such as ExecStart, you must first clear it with an empty assignment (ExecStart=) and then set the new value, or systemd appends rather than replaces.
Diagnose Linux networking with ip and ss instead of ifconfig and netstat, learn why ping proves little, debug DNS with dig and choose rsync over scp.
Find and fix Linux disk problems: why df and du disagree, deleted-but-open files, inode exhaustion, persistent mounts by UUID in fstab and tar versus zip.
Write bash scripts that fail loudly, not silently: use set -euo pipefail, quote variables, prefer [[ ]] over [ ], clean up with trap and lint with shellcheck.