Linux systemctl: Managing systemd Services

systemd has become the de facto init system and service manager for modern Linux distributions. Whether you're running Ubuntu, Fedora, Debian, or Arch Linux, you're almost certainly using systemd. It...

Key Insights

  • systemctl is the central command for managing systemd services, replacing older init systems with a unified interface for starting, stopping, and monitoring services across all major Linux distributions
  • Understanding service persistence through enable/disable commands is critical—starting a service doesn’t mean it will survive a reboot unless explicitly enabled
  • Custom systemd unit files give you fine-grained control over application lifecycle, dependencies, and restart policies, making them superior to traditional init scripts

Understanding systemd and systemctl

systemd has become the de facto init system and service manager for modern Linux distributions. Whether you’re running Ubuntu, Fedora, Debian, or Arch Linux, you’re almost certainly using systemd. It replaced the traditional SysV init system and offers parallel service startup, on-demand daemon activation, and sophisticated dependency management.

The systemctl command is your primary interface to systemd. It controls the systemd system and service manager, allowing you to manage services, examine system state, and modify system behavior. Before diving into specific operations, verify your systemd version:

systemctl --version

This displays the systemd version and enabled features. To get an overview of your system’s current state:

systemctl status

This command shows a tree view of running services, system load, and recent log entries—essentially a health snapshot of your system.

Essential Service Management Operations

The most common systemctl operations involve controlling service states. These commands require root privileges, so you’ll typically need to prefix them with sudo.

Starting a service activates it immediately but doesn’t configure it to start on boot:

sudo systemctl start nginx

Stopping a service terminates it gracefully:

sudo systemctl stop postgresql

Restarting stops and then starts a service, useful when you’ve modified configuration files:

sudo systemctl restart apache2

The reload command is more elegant than restart—it tells the service to reload its configuration without fully stopping:

sudo systemctl reload ssh

Not all services support reload. If a service doesn’t, systemctl will notify you that the operation failed.

Checking service status is crucial for troubleshooting:

systemctl status mysql

This displays whether the service is active, its PID, memory usage, recent log entries, and the CGroup hierarchy. The color-coded output immediately shows if something is wrong—green for active, red for failed.

Configuring Service Persistence

Here’s a critical distinction many Linux administrators miss: starting a service doesn’t mean it will start automatically after a reboot. That requires enabling the service.

Enable a service to start at boot:

sudo systemctl enable docker

This creates symbolic links in the appropriate systemd target directories. The service won’t start immediately—it only configures boot behavior.

To start a service and enable it simultaneously:

sudo systemctl enable --now docker

The --now flag is a time-saver that combines enable and start in one command.

Disabling prevents a service from starting at boot:

sudo systemctl disable bluetooth

Check if a service is enabled:

systemctl is-enabled ufw

This returns “enabled”, “disabled”, or “static” (cannot be enabled/disabled). The exit code is 0 for enabled services, making it useful in scripts.

To see all service unit files and their states:

systemctl list-unit-files --type=service

This comprehensive list shows every service systemd knows about and whether it’s enabled, disabled, masked, or static. You can filter and sort this output with grep and other tools.

Monitoring and Diagnosing Service Issues

systemd integrates tightly with journald, the systemd logging service. This integration makes troubleshooting significantly easier than parsing traditional log files.

Follow a service’s logs in real-time:

sudo journalctl -u nginx -f

The -f flag works like tail -f, continuously displaying new log entries. This is invaluable when debugging service startup issues or monitoring application behavior.

For historical logs, specify a time range:

sudo journalctl -u nginx --since "1 hour ago"

Identify all failed services on your system:

systemctl list-units --failed

This command should be part of your regular system health checks. A failed service might be silently causing issues.

Understanding service dependencies helps diagnose complex startup problems:

systemctl list-dependencies nginx

This shows what services nginx requires and what requires nginx. The tree structure reveals the dependency chain, helping you understand why a service might fail to start.

For exhaustive service information:

systemctl show nginx

This dumps every property systemd tracks for the service—configuration paths, resource limits, execution details, and more. It’s verbose but comprehensive.

Creating Custom Service Units

The real power of systemd emerges when you create custom service units for your applications. This is far superior to cobbling together init scripts or using screen sessions.

Create a service file at /etc/systemd/system/myapp.service:

[Unit]
Description=My Custom Application
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/start.sh
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Let’s break down the critical sections:

The [Unit] section defines metadata and dependencies. After= specifies ordering—your service starts after the network and PostgreSQL. Requires= creates a hard dependency—if PostgreSQL fails, your service stops.

The [Service] section contains execution details. Type=simple means the ExecStart process is the main service process. Other types include forking (for traditional daemons) and oneshot (for tasks that exit).

Restart=on-failure with RestartSec=10 provides automatic recovery—if your application crashes, systemd waits 10 seconds and restarts it. This alone eliminates the need for external process supervisors.

The [Install] section determines where systemd links the service when enabled. multi-user.target is the standard target for multi-user systems.

After creating the service file, reload systemd to recognize it:

sudo systemctl daemon-reload

You must run daemon-reload whenever you create or modify unit files. Then enable and start your service:

sudo systemctl enable --now myapp

Advanced systemctl Capabilities

Masking prevents a service from being started by any means—manually or as a dependency:

sudo systemctl mask apache2

This is stronger than disable. Even systemctl start apache2 will fail. Masking is useful when you want to ensure a conflicting service never runs. Unmask to reverse:

sudo systemctl unmask apache2

Edit service configurations without modifying the original unit file:

sudo systemctl edit nginx

This creates an override file in /etc/systemd/system/nginx.service.d/. Your changes persist across package updates, unlike editing /lib/systemd/system/nginx.service directly.

For quick edits to the entire file:

sudo systemctl edit --full nginx

systemctl also manages system state. Enter rescue mode (single-user mode):

sudo systemctl isolate rescue.target

Reboot the system:

sudo systemctl reboot

Power off:

sudo systemctl poweroff

These commands are cleaner than traditional shutdown or reboot commands and integrate properly with systemd’s shutdown procedures.

Practical Recommendations

Always use enable --now instead of separate enable and start commands. It’s atomic and saves typing.

Make systemctl status your first troubleshooting step. The output usually points directly to the problem.

Use service-specific override files via systemctl edit rather than modifying vendor-supplied unit files. This prevents package managers from overwriting your changes.

When creating custom services, always specify Restart=on-failure and appropriate RestartSec= values. This simple configuration prevents most service availability issues.

Check systemctl list-units --failed regularly, especially on production systems. Failed units often indicate underlying problems that haven’t yet caused visible outages.

For applications you’re developing, create proper systemd unit files from the start. Don’t rely on screen sessions or manual startup scripts. systemd handles logging, restarts, and dependency management better than any custom solution you’ll build.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.