Cloud Explainers

How Linux systemd Works

Linux9 min videoPublished Nov 9, 2026

systemd is the first program Linux starts, and it decides what runs at boot and after a crash. We started a small tea-shop app by hand and lost it in a reboot, then turned it into a systemd service and rebooted for real: PID 1, the unit file section by section, why start isn't enable, boot targets, automatic restarts, and how a cgroup lets systemd stop every process a service started.

How Linux systemd Works, the full picture: diagram of Linux kernel, systemd, Unit file, Boot target, app.py, refresher.py, Journal, the shop's cgroup
The whole picture, from the video's recap.

Gone after a reboot

You start your app by hand on a Linux server, and it works, until the server reboots and the app never comes back. Here's Ana's tea shop. She starts it by hand, the page answers, and then she reboots. After the reboot: nothing is listening, and no Python is running at all.

shop: Ana's server
nohup python3 app.py > shop.log 2>&1 &[1] 121
shop: Ana's server
curl -s localhost:8080Ana's Tea Shop: 12 teas in stock
shop: Ana's server
sudo reboot[sudo] password for ana:
shop: Ana's server
curl -sS localhost:8080curl: (7) Failed to connect to localhost port 8080 after 0 ms: Couldn't connect to server
shop: Ana's server
ps -C python3    PID TTY          TIME CMD

The program that decides what runs on a Linux machine, at boot and after a crash, is systemd. By the end you'll know how it starts things, why starting a service isn't enough, and why one of Ana's processes survives her kill command, but can't survive systemd.

It's one of the oldest questions on Ask Ubuntu: how do I run my scripts when the machine starts, so I don't have to start them by hand every time? Today's answer is a systemd service, so let's build one for Ana's shop, one piece at a time.

A real question, from Ask Ubuntu: cards of Run my scripts at boot, "Not by hand", Today's answer, Our way

Who starts everything?

So who starts everything? Ask Linux about process number one, the first program the kernel runs. It's systemd, started at the moment of that reboot. And it's already running services of its own: the journal that keeps logs, logins, and D-Bus, which lets programs talk to each other.

shop: Ana's server
ps -p 1 -o pid,lstart,comm    PID                  STARTED COMMAND
      1 Mon Nov  9 01:32:54 2026 systemd
shop: Ana's server
systemctl list-units --type=service --state=running --no-legend --plaindbus.service             loaded active running D-Bus System Message Bus
systemd-journald.service loaded active running Journal Service
systemd-logind.service   loaded active running User Login Management

That's the shape of every Linux machine. The kernel starts exactly one program, PID 1. On Ubuntu, Fedora, Debian and most others, that's systemd, and everything else is started by it, or by something it started. It's called the init system: the first process, and the parent of the rest.

One program starts all the others: diagram of Linux kernel, systemd, Journal, Logins, D-Bus

Here's what that means for Ana. She starts the shop by hand again. The app starts a helper that refreshes the stock list, so the helper's parent is the app. She kills the app. Guess first: does the helper die with it? No. It's still running, and its new parent is process one.

shop: Ana's server
nohup python3 app.py > shop.log 2>&1 &[1] 103
shop: Ana's server
ps -o pid,ppid,args -C python3    PID    PPID COMMAND
    103      87 python3 app.py
    104     103 python3 refresher.py
shop: Ana's server
kill 103
shop: Ana's server
ps -o pid,ppid,args -C python3    PID    PPID COMMAND
    104       1 python3 refresher.py
[1]+  Terminated              nohup python3 app.py > shop.log 2>&1

When a parent dies, Linux hands its children to PID 1, so systemd adopts the helper. But nothing records that it belonged to the tea shop. A program started by hand is on its own: no start at boot, no restart after a crash, and leftovers nobody cleans up.

Started by hand: on its own: diagram of systemd, app.py, refresher.py

A unit file: the recipe

So how do you hand a program to systemd? You write a unit file: a short text file in the systemd system folder, named after the service. Ana's is called teashop.service, twelve lines in three sections.

shop: Ana's server
cat /etc/systemd/system/teashop.service[Unit]
Description=Ana's tea shop
After=network.target

[Service]
User=ana
WorkingDirectory=/home/ana/teashop
ExecStart=/usr/bin/python3 app.py
Restart=on-failure

[Install]
WantedBy=multi-user.target

Unit says what it is, and what it waits for: start after the network is up. Service says how to run it: as her own user, not root, from her folder, with this exact command, and restart it if it fails. Install says when it belongs: as part of the normal boot, the multi-user target.

teashop.service, section by section: diagram of what it is, how to run, when it belongs

systemd reads unit files into memory, so after adding one, Ana tells it to re-read them. Then she starts the service, and asks for its status. Active, running. A main PID, and under it a C group holding both processes: the app and its helper. And the shop answers.

shop: Ana's server
sudo systemctl daemon-reload[sudo] password for ana:
shop: Ana's server
sudo systemctl start teashop
shop: Ana's server
systemctl status teashop --no-pager● teashop.service - Ana's tea shop
     Loaded: loaded (/etc/systemd/system/teashop.service; disabled; preset: enabled)
     Active: active (running) since Mon 2026-11-09 01:33:02 UTC; 1s ago
   Main PID: 138 (python3)
      Tasks: 2 (limit: 9563)
     Memory: 12.1M (peak: 12.1M)
        CPU: 21ms
     CGroup: /system.slice/teashop.service
             ├─138 /usr/bin/python3 app.py
             └─139 python3 refresher.py

Nov 09 01:33:02 shop python3[138]: Tea shop open on port 8080
Nov 09 01:33:02 shop python3[139]: stock list refreshed
shop: Ana's server
curl -s localhost:8080Ana's Tea Shop: 12 teas in stock

Where did the app's own words go? When Ana ran it by hand, its output went to a file she picked. As a service, systemd catches everything the app prints and hands it to the journal, stamped with the time, the machine and the process ID. The last lines of the status are those journal lines: tea shop open, stock list refreshed.

A service's output goes to the journal: diagram of app.py, refresher.py, systemd, Journal

Services are one kind of unit. The name's ending says the kind. Dot service runs a program. Dot target groups other units, like the boot we'll meet in a moment. Dot timer runs a service on a schedule, and dot socket starts one when the first connection arrives. systemctl handles them all the same way.

Unit types: the ending says the kind: cards of .service, .target, .timer, .socket

Started is not enabled

But that status said one more word: disabled. Ana started the shop, she didn't enable it, and those are two different things in systemd. Guess first: after a reboot, is it running? No. After the reboot, nothing answers, and systemd says the shop is inactive.

shop: Ana's server
systemctl is-enabled teashopdisabled
shop: Ana's server
sudo reboot
shop: Ana's server
curl -sS localhost:8080curl: (7) Failed to connect to localhost port 8080 after 0 ms: Couldn't connect to server
shop: Ana's server
systemctl is-active teashopinactive

Enable is its own step. It creates one link: the unit file now also appears in a folder called multi-user target dot wants. One more reboot, and this time the shop answers by itself.

shop: Ana's server
sudo systemctl enable teashop[sudo] password for ana:
Created symlink /etc/systemd/system/multi-user.target.wants/teashop.service → /etc/systemd/system/teashop.service.
shop: Ana's server
sudo reboot
shop: Ana's server
curl -s localhost:8080Ana's Tea Shop: 12 teas in stock

So start means now: run it until it stops or the machine goes down. Enable means at every boot, and it doesn't start anything right away. Most of the time you want both, and enable with dash dash now does both in one command.

Start or enable?: comparison table of systemctl start vs systemctl enable, Runs it, After a reboot, What it changes, Both at once

What happens at boot

What does that link do at boot? A target is a goal: a group of units that should be running. systemd heads for the default target, graphical here, which pulls in the multi-user target, which wants every unit linked in its folder, the tea shop included.

Booting is reaching a target: diagram of systemd, graphical.target, multi-user.target, teashop.service

And it's fast. systemd-analyze shows the chain the shop waited for at boot. The journal was up 33 milliseconds after systemd started, the basic target next, and the tea shop at 76 milliseconds. A small container boots quickly; a real server takes seconds, but the order works the same way.

The chain the shop waited for at boot: diagram of teashop.service, basic.target, systemd-journald.service

Order and need are two separate things in a unit file. After equals network target says: start that one first, if both are starting. It doesn't pull the network in. Wants and Requires do that: start that one too. And units with no order between them start at the same time, which is a big part of why systemd boots quickly.

Order is not the same as need: comparison table of After= vs Wants= / Requires=, Says, Pulls it in?, No order at all

This isn't only Ubuntu. Amazon Linux 2023, the Linux Amazon makes for its own cloud servers, keeps its logs in the systemd journal by default, and its user guide tells you to read them with journalctl. Learn these commands once, and they work on most Linux servers you'll meet.

The same tools on Amazon's own Linux: cards of Amazon Linux 2023, systemd journal, journalctl, Learn once

When the app crashes

What if the shop crashes? Ana looks up its main PID and kills it with kill -9, which a program can't catch, like a hard crash. Two seconds later: a different main PID, and a restart count of one. The journal shows why: killed by signal nine, a restart scheduled, started again, and the shop answers.

shop: Ana's server
systemctl show teashop -p MainPIDMainPID=74
shop: Ana's server
sudo kill -9 74[sudo] password for ana:
shop: Ana's server
systemctl show teashop -p MainPID -p NRestartsMainPID=111
NRestarts=1
shop: Ana's server
sudo journalctl -u teashop --no-pager -n 6 -o catteashop.service: Main process exited, code=killed, status=9/KILL
teashop.service: Failed with result 'signal'.
teashop.service: Scheduled restart job, restart counter is at 1.
Started teashop.service - Ana's tea shop.
Tea shop open on port 8080
stock list refreshed
shop: Ana's server
curl -s localhost:8080Ana's Tea Shop: 12 teas in stock

That's the Restart line from the unit file at work. systemd is the app's parent, so it hears the moment the app dies. With restart on failure, a crash, a kill -9, or an error exit gets a fresh start, a tenth of a second later by default. A clean stop with systemctl stop does not.

Restart=on-failure, step by step: diagram of systemd, The app, start, killed, signal 9, start again, systemctl stop

Stop means all of it

Now, that helper that survived Ana's kill. Here's the trick: systemd puts every process a service starts into a control group, a C group: a named box the kernel keeps track of. Here's the shop's box: the app, and its helper. Ana stops the service, and there's no Python left at all.

shop: Ana's server
systemd-cgls -u teashop.service --no-pagerUnit teashop.service (/system.slice/teashop.service):
├─111 /usr/bin/python3 app.py
└─112 python3 refresher.py
shop: Ana's server
sudo systemctl stop teashop
shop: Ana's server
ps -C python3    PID TTY          TIME CMD

That's the answer to the question from the start. A kill reaches one process, and its children get handed to PID 1. A C group can't be escaped that way: whatever a service starts stays in its box, so stop, restart and crash cleanup catch every process, the helper too.

So here's Ana's shop both ways, from this one run. After a reboot, the hand-started shop was gone; the service came back once enabled. After a kill, the hand-started app stayed down; the service restarted itself. The helper outlived Ana's kill, but stopped with the service. And the service's output landed in the journal.

Ana's shop, both ways: comparison table of Started by hand vs systemd service, After a reboot, After a kill, Its helper, Its output

Recap

Every command and the lab files are linked in the description, so you can build this service yourself.

Run it yourself: cards of teashop.service, app.py, Dockerfile.server, Three reboots

So: systemd is PID 1. A unit file says what to run and when. Start is now, enable is every boot, and the C group lets systemd stop all of it.

If Ana's shop made systemd click for you, a like helps YouTube show it to more beginners, and subscribing brings the next Linux demo straight to you.

Every command here ran for real, on a small Ubuntu server we rebooted three times. Thanks for watching Cloud Explainers.

Sources

Demos: a container on our own Mac standing in for Ana's server; the tea shop is made up. Watch the full video on YouTube.

More videos

Linux9 min

Linux Makefiles Will Finally Make Sense After This

Auth (OAuth, JWT)9 min

SAML vs OAuth vs OpenID Connect: What's the Difference?

Python7 min

How to Fix Python cron Jobs That Never Run

Linux22 min

Linux Bash Scripting: Full Tutorial for Beginners

Networking10 min

How PXE Network Boot Works

Active Directory9 min

How Active Directory Works

Linux10 min

How to Fix a Linux systemd Service That Won't Start

Linux9 min

Linux Logs: syslog vs journald

Auth (OAuth, JWT)9 min

JWT vs Session Cookies: Which One Should You Use?

Docker10 min

Docker vs Docker Compose: Which One Should You Use?

Docker9 min

How to Fix Docker Compose Containers That Can't Talk to Each Other

Django (Python web framework)22 min

Django Full Tutorial for Beginners

Networking8 min

How to Fix 'Windows Cannot Access' Network Share Errors (SMB)

APIs10 min

REST vs gRPC: Which One Should You Use?

Certificates & PKI (ADCS)9 min

How to Fix SSL Certificate Chain Errors (Missing Intermediate)

Python9 min

Every Python Concept for Beginners in 10 Minutes

Databases & SQL9 min

Every SQL Join Type in 10 Minutes

OpenRouter (one API for many AI models)9 min

Why OpenRouter Free Models Hit Their Limit So Fast

Gemini API & Google AI Studio9 min

Why Your Gemini API Free Tier Runs Out So Fast

OpenRouter (one API for many AI models)10 min

OpenRouter Will Finally Make Sense After This

Caching & Redis10 min

How Redis Caching Works

Gemini API & Google AI Studio10 min

Gemini API vs Vertex AI: Which One Should You Use?

Gemini API & Google AI Studio8 min

How to Fix a Leaked Gemini API Key

Go (Golang)9 min

Why Golang Uses Goroutines Instead of Threads

Gemini API & Google AI Studio9 min

How to Fix Gemini API Error 429: Resource Exhausted

Claude & the Claude Developer Platform10 min

Claude Token Pricing Will Finally Make Sense After This

Python11 min

Why Python Web Scraping Returns Nothing (and the Fix)

Python10 min

Python uv vs pip: Which One Should You Use?

Python22 min

Python Automation: Full Tutorial for Beginners

Auth (OAuth, JWT)9 min

How OAuth Login Works

Gemini API & Google AI Studio10 min

Google AI Studio Will Finally Make Sense After This

Docker10 min

What Happens When You Run a Docker Container

Claude & the Claude Developer Platform8 min

How to Fix Claude Code Billing Your API Key Instead of Your Plan

Auth (OAuth, JWT)9 min

Why Passkeys Can't Be Phished Like Passwords

PowerShell10 min

PowerShell vs Bash: Which One Should You Learn?

Linux10 min

The Linux Roadmap for Beginners

Django (Python web framework)9 min

Django vs FastAPI: Which One Should You Use?

Databases & SQL10 min

Why SQLite Is the Most Used Database in the World

Databases & SQL11 min

How a NoSQL Database Like MongoDB Works

Python11 min

How to Fix Python pip error: externally-managed-environment

Databases & SQL9 min

How to Fix MySQL Too Many Connections Database Errors

Go (Golang)9 min

Golang Concurrency Will Finally Make Sense After This

AWS10 min

How Amazon S3 Works

Kubernetes10 min

How Kubernetes Works

Git10 min

How Git Works

Docker10 min

How to Fix Docker Port Already Allocated Errors

PostgreSQL10 min

How PostgreSQL Works

Git8 min

How to Fix Git Merge Conflicts

Linux9 min

How to Fix No Space Left on Device in Linux

Networking10 min

How the Internet Works

Databases & SQL9 min

SQL vs NoSQL: When Each One Wins

Auth (OAuth, JWT)8 min

How to Fix JWT Token Expired Errors (401 Unauthorized)

Claude & the Claude Developer Platform9 min

Claude API vs Claude Pro: Why Your Subscription Doesn't Include It

AI agent skills9 min

Why AI Agent Skills Beat Giant Prompts

Linux10 min

Linux Cron Jobs Will Finally Make Sense After This

Cybersecurity10 min

Security Logs Will Finally Make Sense After This

Cybersecurity8 min

How to Fix a Security Leak: An API Key Pushed to GitHub

DNS9 min

DNS Will Finally Make Sense After This

Cybersecurity9 min

Never Ignore a Security Update

Auth (OAuth, JWT)9 min

Two-Factor Authentication Will Finally Make Sense

Networking8 min

Never Expose a Database to the Internet

Cybersecurity10 min

SSH Keys vs Passwords: Which Is Safer for Security?

Linux10 min

The 5 Levels of Linux Boot: From Power On to Login

Networking12 min

The 5 Levels of Network Firewalls

Cybersecurity10 min

Password Security: Why Hackers Crack Weak Passwords

Linux9 min

Why Your Linux Server Runs Out of Memory

Networking10 min

HTTP vs HTTPS: What Actually Changes on Your Network

Linux12 min

Every Linux File Permission You Need to Know

Linux10 min

Linux Processes vs Threads: When Each One Wins

Networking11 min

The OSI Model Will Finally Make Sense After This

Git11 min

The 7 Levels of Git

Networking10 min

Every Network Troubleshooting Command You Need

Cybersecurity10 min

Hashing vs Encryption in Cybersecurity: When Each One Wins

Networking11 min

Why the Internet Ran Out of IP Addresses

Linux11 min

Linux Package Managers: apt vs Snap vs Flatpak

Python9 min

Why Python Is Slow (and When It Doesn't Matter)

Networking11 min

TCP vs UDP: When Each One Wins

Linux8 min

Stop Running Linux as Root (Do This Instead)

Linux11 min

Linux Users and Groups Will Finally Make Sense

Cybersecurity9 min

Why Phishing Beats Good Security (and How to Spot It)

Docker12 min

The 7 Levels of Docker

AI agents & MCP12 min

AI Agents Will Finally Make Sense After This

Git8 min

Stop Using git push --force (Use This Instead)

AI agent skills10 min

Agent Skills vs MCP: When Each One Wins

Networking12 min

Networking Will Finally Make Sense After This

Linux12 min

Every Linux Command You Actually Need

Cybersecurity12 min

The 7 Levels of Cybersecurity

System design12 min

Every System Design Concept You Need, in One Diagram

PostgreSQL10 min

Why PostgreSQL Ignores Your Index