๐Ÿ›Ž๏ธ Lesson 8: Services and Logs: Starting, Stopping and Reading What Your System Says

How the programs that run in the background are managed, and where to look when one of them misbehaves

Video: Services and Logs (Linux 101 video)
Prefer to watch? This short video runs the lesson's main commands on a real Ubuntu 24.04 machine, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.

What is a service?

Some programs are not meant to be opened and closed by you. A service (also called a daemon, pronounced "demon", which sounds scarier than it is) is a program that runs quietly in the background, waiting to do a job for someone else: a web server answering visitors, an SSH server letting you log in from afar, a scheduler running timed jobs. Services start when the machine boots and keep running whether anyone is logged in or not.

Something has to start them, restart them if they crash, and stop them politely when the machine shuts down. On Ubuntu that something is systemd. It is process number 1, the parent of everything (lesson 7), and the command you use to talk to it is systemctl.

๐Ÿ’ก A different practice machine for this lesson. The tiny container from lesson 1 has no systemd, so it cannot run services. For this lesson you need a full Ubuntu: a virtual machine, a real computer, or Windows' WSL with systemd switched on (see Microsoft's documentation; from the documentation, not run here). The examples below, and the video, were run on a full Ubuntu 24.04 LTS (a systemd container) that was set up for this lesson, with a user called ubuntu who is allowed to use sudo and whose password is practice. Your times, process numbers and sizes will differ.

The manager is process number 1

ubuntu@practice:~$ ps -p 1
    PID TTY          TIME CMD
      1 ?        00:00:00 systemd
ubuntu@practice:~$ systemctl list-units --type=service --state=running --no-pager
  UNIT                     LOAD   ACTIVE SUB     DESCRIPTION
  cron.service             loaded active running Regular background program processing daemon
  dbus.service             loaded active running D-Bus System Message Bus
  rsyslog.service          loaded active running System Logging Service
  systemd-journald.service loaded active running Journal Service
  systemd-resolved.service loaded active running Network Name Resolution

Legend: LOAD   โ†’ Reflects whether the unit definition was properly loaded.
        ACTIVE โ†’ The high-level unit activation state, i.e. generalization of SUB.
        SUB    โ†’ The low-level unit activation state, values depend on unit type.

5 loaded units listed.

Compare this with lesson 7, where process 1 was bash. Here process 1 is systemd. systemctl list-units --type=service --state=running lists the services running right now. The word unit is systemd's name for anything it manages, and a service is one kind of unit. The columns:

You can see cron (the scheduler), dbus (the message bus that programs use to talk to each other), rsyslog and systemd-journald (which keep the logs) and systemd-resolved (which looks up names, lesson 9). A desktop or a busy server lists many more. The --no-pager option told systemctl to print everything at once. Without it, when the output is longer than your screen, systemctl opens it in the less viewer from lesson 6: press the Space bar for the next page and q to quit.

Ask a service how it is: status

ubuntu@practice:~$ systemctl status cron --no-pager
โ— cron.service - Regular background program processing daemon
     Loaded: loaded (/usr/lib/systemd/system/cron.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-10-22 11:14:28 UTC; 28s ago
       Docs: man:cron(8)
   Main PID: 80 (cron)
      Tasks: 1 (limit: 5197)
     Memory: 348.0K (peak: 1.4M)
        CPU: 12ms
     CGroup: /system.slice/docker-304dd929d678615f8d7e3d5ec1a8a9b7b96e3c0f2237165fad3cb907f9938ee3.scope/system.slice/cron.service
             โ””โ”€80 /usr/sbin/cron -f -P
ubuntu@practice:~$ systemctl is-active cron
active
ubuntu@practice:~$ systemctl is-enabled cron
enabled
ubuntu@practice:~$ systemctl is-active nosuchservice
inactive
ubuntu@practice:~$ systemctl status nosuchservice --no-pager
Unit nosuchservice.service could not be found.

systemctl status cron is the command you will run most often. (On a real screen the first line starts with a coloured dot, green for healthy. The dot is the โ— you see below.) The long CGroup name is because the lab runs inside a container; on a normal machine it is short, like /system.slice/cron.service. Reading it from the top:

Two shortcuts give a one-word answer, which is handy in scripts: systemctl is-active cron says active, and systemctl is-enabled cron says enabled. If you ask about a service that does not exist, you get a clear message (Unit nosuchservice.service could not be found.), and is-active says inactive.

Start, stop and restart

ubuntu@practice:~$ systemctl stop cron
Failed to stop cron.service: Access denied
See system logs and 'systemctl status cron.service' for details.
ubuntu@practice:~$ sudo systemctl stop cron
[sudo] password for ubuntu:
ubuntu@practice:~$ systemctl is-active cron
inactive
ubuntu@practice:~$ sudo systemctl start cron
ubuntu@practice:~$ systemctl is-active cron
active
ubuntu@practice:~$ sudo systemctl restart cron
ubuntu@practice:~$ systemctl status cron --no-pager
โ— cron.service - Regular background program processing daemon
     Loaded: loaded (/usr/lib/systemd/system/cron.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-10-22 11:15:40 UTC; 4s ago
       Docs: man:cron(8)
   Main PID: 285 (cron)
      Tasks: 1 (limit: 5197)
     Memory: 344.0K (peak: 464.0K)
        CPU: 8ms
     CGroup: /system.slice/docker-304dd929d678615f8d7e3d5ec1a8a9b7b96e3c0f2237165fad3cb907f9938ee3.scope/system.slice/cron.service
             โ””โ”€285 /usr/sbin/cron -f -P

A real example: a web server

Services get interesting when they do something you can see. Let us install a web server. Lesson 6 showed you how, and the output is shortened here (on this machine the package list was already fresh, so there is no sudo apt update first; if yours is old, run it before the install, as in lesson 6. curl, a tool that fetches web pages from the command line, is asked for too, and it was already installed):

ubuntu@practice:~$ sudo apt install -y nginx curl
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
curl is already the newest version (8.5.0-2ubuntu10.16).
Suggested packages:
  fcgiwrap nginx-doc ssl-cert
The following NEW packages will be installed:
  nginx nginx-common
0 upgraded, 2 newly installed, 0 to remove and 3 not upgraded.
Need to get 570 kB of archives.
After this operation, 1602 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 nginx-common all 1.24.0-2ubuntu7.18 [45.1 kB]
Get:2 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 nginx amd64 1.24.0-2ubuntu7.18 [525 kB]
Fetched 570 kB in 2s (288 kB/s)
[... the middle of the output is left out to keep this page short ...]
ubuntu@practice:~$ systemctl is-active nginx
active
ubuntu@practice:~$ curl -sI localhost
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Thu, 22 Oct 2026 11:16:04 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 22 Oct 2026 11:15:55 GMT
Connection: keep-alive
ETag: "6ad9f06b-267"
Accept-Ranges: bytes

The important part is what you did not have to do: nobody started nginx. Installing a service package starts it, and enables it for next time. Look at the rest of the output above. It checked the service with systemctl is-active nginx, which said active, and then asked the web server for a page with curl. The -s option means "silent" (no progress chatter) and -I asks only for the headers, not the page. localhost is the name every computer gives itself, so this means "ask the web server on this machine".

The reply is HTTP/1.1 200 OK, which means "found it, here you go", and Server: nginx/1.24.0 (Ubuntu) shows who answered. systemctl status nginx would also list its main process (the master) and the workers that do the actual work, which is how nginx is built (not shown here).

Now stop it, and watch the website go dark and come back:

ubuntu@practice:~$ sudo systemctl stop nginx
ubuntu@practice:~$ curl -I localhost
curl: (7) Failed to connect to localhost port 80 after 0 ms: Couldn't connect to server
ubuntu@practice:~$ sudo systemctl start nginx
ubuntu@practice:~$ curl -sI localhost | head -n 1
HTTP/1.1 200 OK

Start now, or start at boot? enable and disable

Starting a service and enabling it are different things. Start means "run it now". Enable means "run it every time the machine boots". You can do either without the other:

ubuntu@practice:~$ systemctl is-enabled nginx
enabled
ubuntu@practice:~$ sudo systemctl disable nginx
Synchronizing state of nginx.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install disable nginx
Removed "/etc/systemd/system/multi-user.target.wants/nginx.service".
ubuntu@practice:~$ systemctl is-enabled nginx
disabled
ubuntu@practice:~$ systemctl is-active nginx
active
ubuntu@practice:~$ sudo systemctl enable nginx
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
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service โ†’ /usr/lib/systemd/system/nginx.service.
ubuntu@practice:~$ systemctl is-enabled nginx
enabled

Logs: where to look when something goes wrong

Every service writes down what it is doing: when it started, what it did, what went wrong. Those notes are logs, and reading them is the most important troubleshooting skill you can have. There are two places to look.

The journal, which systemd keeps for everything, and which you read with journalctl:

ubuntu@practice:~$ journalctl -u cron -n 5 --no-pager
Hint: You are currently not seeing messages from other users and the system.
      Users in groups 'adm', 'systemd-journal' can see all messages.
      Pass -q to turn off this notice.
-- No entries --
ubuntu@practice:~$ sudo journalctl -u cron -n 5 --no-pager
Oct 22 11:15:40 practice systemd[1]: Stopped cron.service - Regular background program processing daemon.
Oct 22 11:15:40 practice systemd[1]: Started cron.service - Regular background program processing daemon.
Oct 22 11:15:40 practice (cron)[285]: cron.service: Referenced but unset environment variable evaluates to an empty string: EXTRA_OPTS
Oct 22 11:15:40 practice cron[285]: (CRON) INFO (pidfile fd = 3)
Oct 22 11:15:40 practice cron[285]: (CRON) INFO (Skipping @reboot jobs -- not system startup)
ubuntu@practice:~$ sudo journalctl -u nginx --no-pager
Oct 22 11:15:56 practice systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Oct 22 11:15:56 practice systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.
Oct 22 11:16:09 practice systemd[1]: Stopping nginx.service - A high performance web server and a reverse proxy server...
Oct 22 11:16:09 practice systemd[1]: nginx.service: Deactivated successfully.
Oct 22 11:16:09 practice systemd[1]: Stopped nginx.service - A high performance web server and a reverse proxy server.
Oct 22 11:16:16 practice systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Oct 22 11:16:16 practice systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.
ubuntu@practice:~$ sudo tail -n 3 /var/log/nginx/access.log
::1 - - [22/Oct/2026:11:16:04 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.5.0"
::1 - - [22/Oct/2026:11:16:21 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.5.0"
ubuntu@practice:~$ ls /var/log
README            dmesg             nginx
alternatives.log  dpkg.log          private
apt               faillog           syslog
auth.log          journal           wtmp
bootstrap.log     kern.log
btmp              lastlog
ubuntu@practice:~$ sudo journalctl -p err -b --no-pager
-- No entries --

**Plain text log files in /var/log**, which many programs write themselves:

Finally, if a service failed to start at boot, it shows up in the failed list, and an empty list is the good result:

ubuntu@practice:~$ systemctl --failed --no-pager
  UNIT LOAD ACTIVE SUB DESCRIPTION

0 loaded units listed.

A simple troubleshooting recipe

When a service is not working, follow the same four steps every time:

  1. Is it running? systemctl status NAME
  2. What does it say? sudo journalctl -u NAME -n 30 for the last 30 lines, and look for the word "error" or "failed".
  3. Fix what it says (most often a setting file, which lesson 10 teaches you to edit).
  4. Restart it and check again: sudo systemctl restart NAME, then systemctl status NAME.
Why did the web server go to therapy?<br>
It had too many unresolved connections.

๐Ÿงช Try it yourself

On your full Ubuntu machine:

1. Is the ssh service active and enabled? (Hint: it is named ssh.) 2. Stop nginx, show that curl -I localhost fails, start it again, and show that it works. 3. Disable nginx. Check is-enabled and is-active. What does each say? Then enable it again. 4. Show the last 10 lines of the SSH service's journal. 5. Which line in the nginx access log was written by your last curl? Show the last line of the log.

The cheat sheet

← Lesson 7 · ↑ All lessons · Lesson 9: Disk, Memory and Network Checks →