EngNotebook Logo

Engineering Notebook

Back to home

Systemctl Field Guide

Engineering Note #03

Last updated · June 4, 2026

DockerLinux

When I first encountered systemctl, I thought it was just another command for starting and stopping services. In reality, it's the command-line interface to one of the most fundamental components of a modern Linux system: systemd.

Once you understand what systemd is trying to model, the seemingly large collection of systemctl commands becomes surprisingly coherent.


The Big Picture

A running Linux system is more than just a kernel.

It is also a collection of long-lived processes:

  • SSH server
  • Docker daemon
  • PostgreSQL
  • Nginx
  • Cron jobs
  • Monitoring agents
  • VPN clients

Somebody has to:

  • start them during boot
  • stop them during shutdown
  • restart them when requested
  • track whether they're healthy
  • restart them if they crash
  • understand dependencies between them

Historically this was handled by SysV init scripts.

Today, almost every Linux distribution uses systemd.

You can think of it as the operating system's process orchestrator.

             Linux Kernel
                  │
                  ▼
             systemd (PID 1)
                  │
      ┌───────────┼───────────┐
      ▼           ▼           ▼
   Docker       SSHD      PostgreSQL
      │
      ▼
 Docker containers

Every long-running service on the machine ultimately lives under systemd's supervision.


systemd is the Manager

A useful mental model is:

systemd manages services the same way Docker manages containers.

Docker manages containers.

systemd manages processes.

Docker keeps metadata describing containers.

systemd keeps metadata describing services.

Docker knows:

  • how to start a container
  • how to stop it
  • whether it's running

systemd knows:

  • how to start a service
  • how to stop it
  • whether it's running
  • what it depends on

The concepts are remarkably similar.


systemctl is the Client

If systemd is the manager, then systemctl is simply its command-line client.

You
 │
 ▼
systemctl
 │
 ▼
systemd
 │
 ▼
Services

You almost never interact with systemd directly.

Instead, you ask systemctl to send commands to it.

For example:

systemctl start docker

means

"Hey systemd, please start the Docker service."


Service Units

systemd models everything as units.

There are many kinds:

  • service
  • socket
  • timer
  • mount
  • target
  • device

The one you'll encounter most often is the service unit.

A service unit is simply a configuration file describing how to manage a process.

For example:

/etc/systemd/system/immyria.service

contains:

[Service]
WorkingDirectory=/opt/immyria
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down

This is not executable code.

It is declarative configuration.

You're describing:

  • what to run
  • how to stop it
  • what it depends on

systemd interprets this description.


What start Actually Does

Suppose we run

systemctl start immyria

Internally, the flow is approximately:

systemctl
      │
      ▼
systemd
      │
      ▼
Read immyria.service
      │
      ▼
Run ExecStart
      │
      ▼
docker compose up -d

systemd isn't magical.

It simply executes the commands described in the unit file.


Why Not Run Docker Compose Directly?

You certainly could.

docker compose up -d

works perfectly.

The advantage of systemd is that it centralizes lifecycle management.

Instead of remembering how to start every application individually:

docker compose up

postgres -D ...

node server.js

nginx

everything becomes:

systemctl start ...

Every service on the machine exposes the same interface.


Common Commands

Start a service:

systemctl start immyria

Stop it:

systemctl stop immyria

Restart it:

systemctl restart immyria

Reload it (if supported):

systemctl reload immyria

Enable it at boot:

systemctl enable immyria

Disable automatic startup:

systemctl disable immyria

Check whether it's running:

systemctl status immyria

Enabled vs Running

One subtle distinction often catches people out.

These are different concepts.

Enabled

means

Start this service automatically during boot.

Running

means

The service is active right now.

This leads to four possible states.

| Enabled | Running | Meaning | | --- | --- | --- | | ❌ | ❌ | Installed but inactive | | ✅ | ❌ | Will start next boot | | ❌ | ✅ | Started manually | | ✅ | ✅ | Running now and will survive reboots |

Ansible reflects this distinction:

enabled: true
state: started

These are two independent settings.


Your Docker Service

Your unit file is unusual in one important way.

Type=oneshot
RemainAfterExit=yes

Normally, systemd supervises a long-running process.

For example:

systemd
    │
    ▼
nginx

The nginx process remains alive forever.

Your service behaves differently.

systemd
    │
    ▼
docker compose up -d
        │
        ▼
command exits

The command finishes almost immediately.

However, Docker has already created background containers.

The actual running processes are now:

systemd
    │
    ▼
Docker daemon
    │
    ▼
Containers

systemd is no longer supervising the containers directly.

Instead, it is supervising the Compose lifecycle.

That's why your service reports:

Active: active (exited)

The command completed successfully.

The containers continue running independently.


Dependencies

One of systemd's strengths is dependency management.

Your unit declares:

Requires=docker.service
After=docker.service

This tells systemd:

docker.service
        │
        ▼
immyria.service

Docker must exist.

Docker must already be running.

Only then should the Docker compose stack start.

Instead of writing shell scripts that poll for readiness, you express the dependency graph declaratively.


Journaling

systemd also centralizes logging.

Every service writes into the system journal.

Instead of searching through /var/log, you can inspect logs for a single service:

journalctl -u immyria

Or follow them live:

journalctl -fu immyria

Because systemd launched the service, it already knows which logs belong to it.


Why This Fits the Linux Philosophy

The elegance of systemd lies in standardization.

Every application—whether it's Docker, PostgreSQL, OpenSSH, or your own software—exposes the same operational interface.

systemctl start
systemctl stop
systemctl restart
systemctl status
systemctl enable
systemctl disable

As an operator, you don't need to remember bespoke commands for every piece of software. You learn one lifecycle API, and systemd applies it consistently across the entire operating system.

For infrastructure projects running on Docker compose, this consistency is valuable. Rather than treating your application as "special," you integrate it into the operating system's native service model, allowing it to participate naturally in boot ordering, dependency management, logging, and lifecycle control alongside every other system service.

More Notes

Architecture Diagrams Are Answering the Wrong Question

Engineering Note #01

ArchitectureMedia SystemsDocker

SRT Field Guide

Engineering Note #02

Media SystemsDocker
See all notes

Built by Jonah Sol