Service Supervision, Units and Managers
A server can be running as a process without being supervised or ready to serve a client. A service manager connects desired activation, process lifecycle, logs and failure information. This chapter builds a small local service that subsequent chapters will inspect and improve.
Unit state is not application health
On a systemd host, a unit describes an object managed by systemd. Service units supervise processes; other unit types represent sockets, timers, mounts, devices and targets. The system manager handles system units, while a user’s manager handles that user’s units. systemctl --user addresses the current user’s manager when a user session and bus are available; it is not the same manager as sudo systemctl.
Inspect the system without making changes:
ps -p 1 -o pid,comm,args
systemctl list-units --type=service --state=running
systemctl list-unit-files --type=service
systemctl --failedLoaded units and installed unit files are different inventories. A unit’s active state, its enabled state and its client-visible health are separate facts. enabled generally describes links for future activation, not a promise that the service is running now; a disabled unit can still be started manually or by another unit. A static unit may be activated by a dependency despite lacking direct enablement. systemctl’s manual documents these states.
Prepare a separate service lab
Create bl06-service-lab as a full clone of powered-off bl02, following the same single-disk, no-shared-disk, no-backing-chain and no-TPM precautions used for bl05-packages-lab. Confirm sufficient space and that the destination name and disk path do not already exist:
sudo virsh --connect qemu:///system domstate bl02
sudo virsh --connect qemu:///system domblklist bl02 --details
sudo virt-clone --connect qemu:///system \
--original bl02 --name bl06-service-lab \
--file /var/lib/libvirt/images/bl06-service-lab.qcow2Run the clone command only after checking that the original is shut off and has the expected disk layout. Inspect both domains for distinct disks and NVRAM; keep bl02 and other clones off when this clone runs, because the copied guest has the same identity. The guest hostname may still be bl02: identify it through the VM manager and console, not the hostname alone. Keep a recovery copy and console access. The clone needs Python 3; check command -v python3 and /usr/bin/python3 --version. Do not install tools solely to hide an unexpected baseline.
Create a bounded example service
The example serves existing documentation on loopback port 18765, only inside the VM. Python’s demonstration HTTP server is not a production web server. Verify that port is unused and the name is not already owned before proceeding:
sudo ss -ltnp 'sport = :18765'
systemctl cat bl06-demo.service
ls -ld /usr/share/docAn empty listener result and a unit-not-found result are expected. If the port, unit, or /etc/systemd/system/bl06-demo.service already exists, stop and choose a fresh clone. Create the local unit:
sudo tee /etc/systemd/system/bl06-demo.service > /dev/null <<'UNIT'
[Unit]
Description=BL06 loopback documentation demonstration
[Service]
Type=exec
DynamicUser=yes
WorkingDirectory=/usr/share/doc
ExecStart=/usr/bin/python3 -m http.server 18765 --bind 127.0.0.1
Restart=on-failure
RestartSec=2s
[Install]
WantedBy=multi-user.target
UNIT
sudo systemd-analyze verify /etc/systemd/system/bl06-demo.serviceIf verification reports errors, fix them before loading. DynamicUser=yes uses a transient service identity; this service needs only read access to the documentation directory. It is deliberately not enabled at boot. Load the definition and start it:
sudo systemctl daemon-reload
sudo systemctl start bl06-demo.service
systemctl status bl06-demo.service --no-pager
systemctl is-enabled bl06-demo.service
sudo journalctl -u bl06-demo.service -b --no-pageris-enabled returns a nonzero status for a disabled unit; that is expected here. Test from inside the same guest:
python3 - <<'PYCODE'
import urllib.request
response = urllib.request.urlopen("http://127.0.0.1:18765/", timeout=5)
print(response.status)
PYCODEExpect HTTP 200 for the directory listing. If it fails, inspect the unit status, log, listener address and permissions. Do not expose this example to an untrusted network. Keep the unit for the next chapters; later exercises will remove it.
Practice and recall
Record the manager, unit file, main PID, active and enabled states, listener and client response. Explain why starting a process and obtaining a successful HTTP response answer different questions. Compare systemctl list-units with systemctl list-unit-files and describe why their counts differ.