Services with systemd & systemctl

Linux · Lesson 20 of 32 7 min read Updated

A service (also called a daemon) is a program that runs in the background without a terminal: the web server, the database, the SSH server, the firewall. Something has to start these at boot, restart them if they crash, and let administrators control them. On Ubuntu and nearly every modern distribution, that something is systemd, and you talk to it with the systemctl command.

In this lesson you will check, start, stop and enable services, understand what systemctl status tells you, and write a unit file for your own app.

systemd and units

systemd is the first process the kernel starts (PID 1). It starts everything else and keeps track of it all. Ubuntu 26.04 ships systemd 259. systemd manages units, each described by a small text file. The kinds you will meet most are:

  • .service: a program to run, such as nginx.service or ssh.service
  • .timer: a schedule that starts a service (see Scheduling: cron & systemd Timers)
  • .target: a group of units representing a system state, such as multi-user.target (a normal server, no desktop)
  • .socket and .mount: network sockets and mounted filesystems

Unit files from packages live in /usr/lib/systemd/system/. Your own units and changes go in /etc/systemd/system/, which takes priority and is never overwritten by package updates.

Checking a service

Terminal
systemctl status nginx
Output
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-10-30 08:01:14 IST; 2h 41min ago
       Docs: man:nginx(8)
   Main PID: 845 (nginx)
      Tasks: 3 (limit: 4558)
     Memory: 4.2M (peak: 6.1M)
        CPU: 41ms
     CGroup: /system.slice/nginx.service
             ├─845 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─846 "nginx: worker process"
             └─847 "nginx: worker process"

Oct 30 08:01:14 shop-server systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Oct 30 08:01:14 shop-server systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.

Read it from the top. The green dot means healthy (a red one means failed). Loaded shows the unit file and two important words: enabled (it starts at boot) and the vendor preset. Active: active (running) and the time since it started tell you it is up. Main PID and the CGroup tree list its processes. The last lines are the most recent log messages for this service, which is often where you find why something failed. Press q if the output opens in a pager.

You can use the name with or without .service. For scripts, two quiet checks are handy: systemctl is-active nginx prints active or inactive, and systemctl is-enabled nginx prints enabled or disabled.

Starting, stopping and enabling

Two separate questions apply to every service: is it running now? and will it start at boot?

CommandEffect
sudo systemctl start nginxStart it now
sudo systemctl stop nginxStop it now
sudo systemctl restart nginxStop and start again (short downtime)
sudo systemctl reload nginxRe-read config without stopping, if the service supports it
sudo systemctl enable nginxStart automatically at every boot
sudo systemctl disable nginxDo not start at boot
sudo systemctl enable --now nginxEnable and start in one step
sudo systemctl mask nginxBlock it from being started at all, even by other units
Terminal
sudo systemctl enable --now nginx
sudo nginx -t && sudo systemctl reload nginx
Output
Synchronizing state of nginx.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

The second line is a habit worth copying: test the configuration first (nginx -t), and reload only if the test passes. A broken config plus restart means your website goes down; a broken config caught by the test means nothing changes.

Listing services and finding failures

Terminal
systemctl list-units --type=service --state=running
systemctl --failed
Output
  UNIT                      LOAD   ACTIVE SUB     DESCRIPTION
  cron.service              loaded active running Regular background program processing daemon
  nginx.service             loaded active running A high performance web server and a reverse proxy server
  ssh.service               loaded active running OpenBSD Secure Shell server
  systemd-journald.service  loaded active running Journal Service
...
  UNIT                 LOAD   ACTIVE SUB    DESCRIPTION
● sweets-api.service   loaded failed failed Sharma Sweets order API

systemctl --failed is the first thing to run after a reboot that "did not come back right". To see why a service failed, read its log with journalctl -u sweets-api, explained fully in Logs & journalctl.

Writing your own service

Suppose Priya has a small Python API in /opt/sweets-api. Running it in a terminal stops when she logs out. A unit file makes systemd run it as a proper service: started at boot, restarted on crash, logged to the journal. Create /etc/systemd/system/sweets-api.service:

Text
[Unit]
Description=Sharma Sweets order API
After=network-online.target
Wants=network-online.target

[Service]
User=sweets
WorkingDirectory=/opt/sweets-api
Environment=APP_ENV=production
ExecStart=/opt/sweets-api/.venv/bin/python app.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

[Unit] describes the service and its ordering: start after the network is up. [Service] says how to run it: as the unprivileged user sweets, from which folder, with which command (always a full path), and to restart it five seconds after a crash. [Install] says which target pulls it in when you enable it. Then tell systemd about the new file and start it:

Terminal
sudo systemctl daemon-reload
sudo systemctl enable --now sweets-api
systemctl status sweets-api --no-pager
Output
● sweets-api.service - Sharma Sweets order API
     Loaded: loaded (/etc/systemd/system/sweets-api.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-10-30 11:05:42 IST; 3s ago
   Main PID: 3391 (python)
...

Run daemon-reload after every change to a unit file; otherwise systemd keeps using the old version.

Power and targets

systemctl also controls the machine itself: sudo systemctl reboot restarts and sudo systemctl poweroff shuts down (sudo reboot and sudo shutdown -h now do the same). systemctl get-default shows the target the system boots into: graphical.target on desktops, multi-user.target on servers.

Common mistakes

  • Starting but not enabling. The service runs today and is missing after the next reboot. Use enable --now.
  • Forgetting daemon-reload. systemd warns that the unit file changed on disk and keeps the old settings.
  • Relative paths in ExecStart. systemd does not use your PATH or current folder; write full paths.
  • Running your app as root. Create a dedicated user and set User=, so a bug in the app cannot take over the server.
  • Restarting with a broken config. Test first (nginx -t, apachectl configtest, sshd -t).

Key points

  • systemd (PID 1) manages services and other units; systemctl controls it.
  • systemctl status shows whether a service is enabled, running, its PIDs and recent log lines.
  • start/stop/restart/reload act now; enable/disable decide what happens at boot; enable --now does both.
  • Your own unit files go in /etc/systemd/system/; run daemon-reload after editing them.
  • Use systemctl --failed and journalctl -u name to troubleshoot.

Exercise

Write a systemd service called backup-notes.service that runs the command /usr/bin/tar -czf /home/priya/notes-backup.tar.gz -C /home/priya notes once as user priya. Use Type=oneshot, which means "run the command and finish" instead of staying running. Then load it, run it once, and check whether it succeeded.

Show solution

Create /etc/systemd/system/backup-notes.service:

Text
[Unit]
Description=Back up Priya's notes folder

[Service]
Type=oneshot
User=priya
ExecStart=/usr/bin/tar -czf /home/priya/notes-backup.tar.gz -C /home/priya notes
Terminal
sudo systemctl daemon-reload
sudo systemctl start backup-notes
systemctl status backup-notes --no-pager
Output
○ backup-notes.service - Back up Priya's notes folder
     Loaded: loaded (/etc/systemd/system/backup-notes.service; static)
     Active: inactive (dead)
...
Oct 30 11:20:03 laptop systemd[1]: Finished backup-notes.service - Back up Priya's notes folder.

A oneshot service shows inactive (dead) after it finishes, and the log line "Finished" confirms success. It has no [Install] section, so it is static: it runs only when started by hand or by a timer, which you will add in the scheduling lesson.

Quick Quiz

1. Which command makes a service start automatically at boot?

2. You edited /etc/systemd/system/app.service. What must you run before restarting the service?

3. Where should your own unit files go?

4. What is the safest way to apply a changed nginx config?

Learn Linux with a mentor

Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call