Service Management (systemd)

Skill Level: Fundamental

1. Overview

Systemd is the "init" system for RHEL 10 (ever since RHEL 7 for that matter). Long ago it replaced Upstart, the SysV "init" system used in releases of RHEL 6.x and older. Systemd is more than just a facility to bring up user space - it is a system manager that offers:

  • service parallelization

  • socket and D-Bus activation

  • on-demand starting of services

  • track services and child processes via cgroups

  • and much more

2. Getting Started

For these exercises, you will be using the host node1 as user root.

From host bastion, ssh to node1.

ssh node1

Use sudo to elevate your privileges.

[[ "$UID" == 0 ]] || sudo -i

Verify that you are on the right host for these exercises.

workshop-systemd-checkhost.sh

You are now ready to proceed with these exercises.

3. Core Concepts

3.1. Units

The fundamental building block that systemd manages is called a "unit". A "unit" can describe different types of objects, but the most common type is a "service".

A "unit file" is the configuration file that describes a unit and tells systemd what dependencies exist and how to start, stop and monitor the object.

"unit files" are stored in 2 different directories. One location is reserved for the default configurations as shipped by Red Hat and the other is for customization by the local administrators.

  • Red Hat unit files: /usr/lib/systemd/system/…​

  • Customizations: /etc/systemd/system/…​

3.2. Targets

systemd has a concept similar to SysV init runlevels, called targets. systemd will boot to the “default target” which can be configured using the systemctl set-default command. Some common targets and their equivalent SysV runlevels are:

  • multi-user.target == runlevel 3

  • graphical.target == runlevel 5

Let’s view the current default target.

systemctl get-default
multi-user.target

3.3. Services

As mentioned above, systemd has another concept called a service. A service is a type of unit which defines the traditional daemon or process. Now let us look at what services are running on the system:

systemctl -t service | head -n 15
UNIT                               LOAD   ACTIVE SUB     DESCRIPTION
  UNIT                                                  LOAD   ACTIVE SUB     DESCRIPTION
  auditd.service                                        loaded active running Security Audit Logging Service
  chronyd.service                                       loaded active running NTP client/server
  cloud-config.service                                  loaded failed failed  Cloud-init: Config Stage
  cloud-final.service                                   loaded failed failed  Cloud-init: Final Stage
  cloud-init-local.service                              loaded failed failed  Cloud-init: Local Stage (pre-network)
  cloud-init.service                                    loaded failed failed  Cloud-init: Network Stage
  crond.service                                         loaded active running Command Scheduler
  dbus-broker.service                                   loaded active running D-Bus System Message Bus
  dracut-shutdown.service                               loaded active exited  Restore /run/initramfs on shutdown
  firewalld.service                                     loaded active running firewalld - dynamic firewall daemon
  fwupd.service                                         loaded active running Firmware update daemon
  getty@tty1.service                                    loaded active running Getty on tty1
  gssproxy.service                                      loaded active running GSSAPI Proxy Daemon
  irqbalance.service                                    loaded active running irqbalance daemon

Next let’s view all of the services available (ie: everything installed, running or not) on the system. The following command is similar to the older chkconfig --list as it will show both enabled and disabled services:

systemctl --no-pager list-unit-files -t service
UNIT FILE                                             STATE           PRESET
audit-rules.service                                   enabled         enabled
auditd.service                                        enabled         enabled
auth-rpcgss-module.service                            static          -
autovt@.service                                       alias           -
blk-availability.service                              disabled        disabled
capsule@.service                                      static          -
chrony-wait.service                                   disabled        disabled
chronyd-restricted.service                            disabled        disabled
chronyd.service                                       enabled         enabled
cloud-config.service                                  enabled         enabled
cloud-final.service                                   enabled         enabled
cloud-init-hotplugd.service                           static          -
cloud-init-local.service                              enabled         enabled
cloud-init.service                                    enabled         enabled

...<output truncated>...

The state will be enabled, disabled, static, or masked. Static indicates that the unit file does not contain an "install" section used to enable the unit. In this case, the unit typically performs a one-off action or is used as a dependency of another unit and should not be run by itself.

4. Analyzing System Startup

Let’s collect some initial data about the boot process

systemd-analyze
Startup finished in 1.582s (kernel) + 2.850s (initrd) + 46.531s (userspace) = 50.963s
multi-user.target reached after 41.873s in userspace.

Next let’s inspect further details about all running units, ordered by the time they took to initialize.

systemd-analyze blame | head -n 15
10.613s kdump.service
 3.876s dev-ttyS1.device
 3.876s sys-devices-platform-serial8250-serial8250:0-serial8250:0.1-tty-ttyS1.device
 3.871s dev-ttyS2.device
 3.871s sys-devices-platform-serial8250-serial8250:0-serial8250:0.2-tty-ttyS2.device
 3.843s sys-devices-pnp0-00:00-00:00:0-00:00:0.0-tty-ttyS0.device
 3.843s dev-ttyS0.device
 3.840s dev-ttyS3.device
 3.840s sys-devices-platform-serial8250-serial8250:0-serial8250:0.3-tty-ttyS3.device
 3.839s sys-module-configfs.device
 3.758s dev-virtio\x2dports-org.qemu.guest_agent.0.device
 3.758s sys-devices-pci0000:00-0000:00:02.4-0000:05:00.0-virtio1-virtio\x2dports-vport1p1.device
 3.758s dev-vport1p1.device
 3.708s sys-devices-pci0000:00-0000:00:04.2-0000:0b:00.0-virtio7-block-vdf.device
 3.708s dev-disk-by\x2ddiskseq-6.device

This helps to learn the “cost” of some of the default services. To speed up boot-time, unnecessary services could potentially be removed or disabled.

5. Enable a LAMP Stack

5.1. Install Packages

Now that we have a good idea of what’s installed on our system, let’s get a basic LAMP stack up and running.

You may have already installed all (or a portion) of these packages in previous exercises, but we’ll just do it again for completeness

Let us install some packages.

dnf install -y httpd mariadb-server mariadb php
...<output truncated>...

Package httpd-2.4.63-1.el10.x86_64 is already installed.
Package mariadb-server-3:10.11.11-1.el10.x86_64 is already installed.
Package mariadb-3:10.11.11-1.el10.x86_64 is already installed.
Package php-8.3.15-1.el10.x86_64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!

5.2. Enable Services

Now it’s time to enable the relevant system services. To enable and start a service at the same time, use the --now option.

systemctl enable --now httpd mariadb
Created symlink /etc/systemd/system/multi-user.target.wants/httpd.service → /usr/lib/systemd/system/httpd.service.
Created symlink /etc/systemd/system/mysql.service → /usr/lib/systemd/system/mariadb.service.
Created symlink /etc/systemd/system/mysqld.service → /usr/lib/systemd/system/mariadb.service.
Created symlink /etc/systemd/system/multi-user.target.wants/mariadb.service → /usr/lib/systemd/system/mariadb.service.

Now let’s check the status. You should see two separate sections in the output, one for httpd and one for mariadb.

systemctl --no-pager status httpd mariadb
● httpd.service - The Apache HTTP Server
     Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; preset: disabled)
    Drop-In: /etc/systemd/system/httpd.service.d
             └─php-fpm.conf
     Active: active (running) since Thu 2025-05-01 14:53:17 UTC; 26s ago
 Invocation: cd8bdc7ee14a4efd852e156570cc1e4f
       Docs: man:httpd.service(8)
   Main PID: 58342 (httpd)
     Status: "Total requests: 0; Idle/Busy workers 100/0;Requests/sec: 0; Bytes served/sec:   0 B/sec"
      Tasks: 177 (limit: 45075)
     Memory: 14M (peak: 14.5M)
        CPU: 149ms
     CGroup: /system.slice/httpd.service
             ├─58342 /usr/sbin/httpd -DFOREGROUND
             ├─58402 /usr/sbin/httpd -DFOREGROUND
             ├─58403 /usr/sbin/httpd -DFOREGROUND
             ├─58404 /usr/sbin/httpd -DFOREGROUND
             └─58430 /usr/sbin/httpd -DFOREGROUND

...<output truncated>...

5.3. Enable Firewall

Last but not least, you need to enable a firewall port. If you are progressing through these exercises sequentially, you may not be familiar the with the firewall system in RHEL just yet. We will withhold further discussion on the topic until then. For now, just run the following command to make the service accessible.

firewall-cmd --add-service=http
success

And that’s it. The needed services are running and network ports are accessible.

6. Conclusion

This concludes the first exercises related to systemd.

Time to finish this unit and return the shell to it’s home position.

workshop-finish-exercise.sh

7. Further Reading

You can find more information:

End of Unit