Managing systemd Services: Start, Stop, Status, Logs
AI generated
$
/etc
Linux · systemd · systemctl · journalctl
Managing systemd Services
Start, stop, status and logs under control

Anyone who steers systemd services by trial and error wastes valuable time during outages in production. systemctl starts, stops and monitors services reliably, reads status output correctly, and recognizes states like failed or activating instantly and without guesswork. journalctl then delivers the matching, filtered logs for fast troubleshooting in production Linux environments and Magento deployments.

14 min read systemctl · journalctl · unit files systemd 245+ · Ubuntu · Debian · RHEL

1. Why systemd service management belongs in every admin's toolkit

systemd has been the init process and service manager of nearly every mainstream Linux distribution for years now: Ubuntu, Debian, RHEL, CentOS, Fedora, SUSE. Anyone administering a server or running applications like PHP-FPM, Nginx, MySQL, Varnish, or a custom Magento queue consumer cannot avoid systemctl. Unlike the old SysVinit system with its shell scripts under /etc/init.d/, systemd offers a unified, declarative interface for every service, regardless of whether it should start automatically at boot, depend on other services, or restart itself automatically after a crash.

The practical benefit shows up most clearly during an incident: a storefront stops responding, a consumer stops processing messages, a cron job doesn't run. In all of these situations, the first step is almost always the same: check the affected service with systemctl status, review the last log lines, and correctly classify the state. Anyone who has this workflow down saves valuable minutes during an incident, instead of manually searching process lists or blindly tailing log files.

2. Core systemctl commands: start, stop, restart

The four core commands for daily operations are systemctl start, systemctl stop, systemctl restart and systemctl reload. start starts a service that is currently not running, stop shuts it down in a controlled way using the signal defined in the unit file, usually SIGTERM followed by SIGKILL once a timeout expires. restart is functionally a stop followed by a start and therefore always involves a brief moment of downtime. reload, on the other hand, signals the running process to re-read its configuration without terminating the process itself, which avoids dropped connections for Nginx or PHP-FPM.

For automation, the exit code of every command matters: systemctl restart returns 0 on success and a non-zero value on failure, which can be checked directly in deploy scripts with set -e. It is also important to understand the difference between systemctl start service and systemctl start service.service: if no suffix is given, systemctl automatically appends .service, but for timers, sockets or mount units the suffix must be given explicitly, otherwise the wrong unit gets addressed by accident.


# Start, stop and restart a single service
sudo systemctl start php8.3-fpm
sudo systemctl stop php8.3-fpm
sudo systemctl restart php8.3-fpm

# Reload config without downtime (only if the service supports it)
sudo systemctl reload nginx

# Combination of reload and restart as a safe fallback
sudo systemctl reload-or-restart nginx

# Check the exit code, e.g. inside a deploy script
if ! sudo systemctl restart php8.3-fpm; then
  echo "[ERROR] php8.3-fpm restart failed" >&2
  exit 1
fi

# Manage several services in a single call
sudo systemctl restart nginx php8.3-fpm supervisor

3. Reading service status correctly: Active, Loaded, Main PID

systemctl status service.service delivers far more information than a simple "running" or "not running" state. The Loaded: line shows whether the unit file was found and parsed, plus the current enabled status. The Active: line is the most important one: it shows the combined state of a high level status (active, inactive, failed) and a low level detail in parentheses (running, exited, dead), together with the time elapsed since the last state change. A service can be active (running) when it is meant to stay up permanently, or active (exited) when it is a oneshot service that runs once and then exits, without that being an error at all.

The Main PID: line shows the process ID of the main process together with the actual running command, which helps locate the service in ps or htop. Below that, systemd lists the current cgroup hierarchy with all child processes, which is especially useful for services with several worker processes, such as PHP-FPM or Supervisor. At the end of the output, the last log lines from the journal appear automatically, so an initial diagnosis often does not even require switching to journalctl.


$ systemctl status nginx.service
● nginx.service - A high performance web server and reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2026-07-12 08:14:03 UTC; 2h 11min ago
       Docs: man:nginx(8)
    Process: 1042 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 1055 (nginx)
      Tasks: 5 (limit: 4915)
     Memory: 12.4M
        CPU: 891ms
     CGroup: /system.slice/nginx.service
             ├─1055 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─1056 nginx: worker process
             └─1057 nginx: worker process

Jul 12 08:14:03 web01 systemd[1]: Starting A high performance web server...
Jul 12 08:14:03 web01 systemd[1]: Started A high performance web server and reverse proxy server.

4. enable, disable and the role of symlinks

A service can be running without starting automatically on the next boot, and conversely, it can be scheduled for boot without currently being active. systemctl start and systemctl enable are therefore independent switches for two separate questions: is the service running right now, and should it start automatically at system boot. systemctl enable technically creates a symlink from the unit file into the appropriate .wants directory of the configured target, usually /etc/systemd/system/multi-user.target.wants/. systemctl disable removes exactly that symlink again, without affecting the currently running process.

For deploy automation, systemctl enable --now redis-server is the convenient combo command that sets the symlink and starts the service in the same step, while systemctl disable redis-server removes the symlink again without stopping the running process. Whether a service is currently enabled for autostart is shown by systemctl is-enabled redis-server, and the running state comes from systemctl is-active redis-server. After any change to a unit file itself, for example adjusting ExecStart or Restart, systemctl daemon-reload must be run so systemd re-reads the file. If this step is forgotten, systemd keeps working with the old, cached definition held in memory, which leads to confusing situations in which a configuration change appears to have no effect at all. An overview of every service enabled for autostart comes from systemctl list-unit-files --state=enabled.

5. Analyzing logs with journalctl -u

journalctl is the central tool for reading logs that systemd stores in the binary, structured systemd journal. The most important filter for everyday work is -u, which restricts the output to a single unit: journalctl -u nginx.service shows only log entries from that service, regardless of whether they were written via stdout, stderr, or the native systemd logging API. This replaces the tedious grep-filtering across shared log files where several services mix their output together.

The -f flag follows the log live, similar to tail -f, and is the standard way to watch output during a deployment or restart. With --since and --until the time range can be narrowed precisely, and relative expressions like "1 hour ago" work just as well as absolute timestamps. For structured post-processing, -o json emits every log line as its own JSON object with all metadata, such as PID, priority and boot ID, which feeds nicely into log aggregators like Fluentd or Logstash. -p err additionally filters by priority and shows only errors and more critical messages.


# Follow a service's log live
journalctl -u php8.3-fpm.service -f

# Only error priority and higher, from the last hour
journalctl -u php8.3-fpm.service -p err --since "1 hour ago"

# Logs since the last boot, with precise local timestamps
journalctl -u nginx.service -b --output=short-precise

# Logs from several units, merged and sorted by time
journalctl -u nginx.service -u php8.3-fpm.service --since today

# Cap journal size to keep disk usage under control
sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=14d

6. Understanding service states: failed, activating, dead

The failed state means the service was started but exited with an error, or an ExecStart command itself failed. A failed state persists until it is either manually reset with systemctl reset-failed or the service is successfully restarted. The activating state is, for most services, a brief transitional state during startup, but for misconfigured services with slow health checks it can remain for minutes, especially for services of type notify, which wait for a readiness signal from the process itself.

dead simply describes a stopped service without any error, for example after a planned systemctl stop. The difference between inactive and failed is crucial for monitoring systems: inactive is usually an intentional state, failed is always an alarm signal. systemctl --failed lists every unit in a failure state across the whole server and is therefore the fastest first command to run during a general outage report, before checking individual services in detail. A failed state can be cleared specifically with systemctl reset-failed myapp-worker.service, and inside a script the current state can be queried and evaluated directly via systemctl is-active myapp-worker.service.

7. Dependencies, timeouts and restart policies

systemd models dependencies between services explicitly through directives like Requires=, Wants= and After=. Requires= enforces that the dependent unit must also start, otherwise the start fails; if the dependency later fails, the dependent unit is stopped along with it. Wants= is the weaker, recommended variant for most use cases: it attempts to start the other unit as well, but does not fail if that does not succeed. After= and Before= control only the startup order, without creating a dependency in the "must be running" sense on their own, which is why Wants= and After= are usually combined together in practice.

For fault tolerance, the Restart= directive is decisive: Restart=on-failure only restarts the service automatically after a failure state, while Restart=always does so even after a regular exit code of 0. RestartSec= defines the wait time between restart attempts, while StartLimitBurst= and StartLimitIntervalSec= prevent a permanently crashing service from driving systemd into an infinite restart loop that puts the server under load.

8. Writing custom unit files for cron jobs and queues

For long-lived background processes like a Magento message queue consumer, a dedicated unit file is more robust than a cron job using nohup or a manually started screen session. Custom unit files belong in /etc/systemd/system/, not in /lib/systemd/system/, which stays reserved for the package manager. The [Service] block with Type=simple defines the standard case of a process running in the foreground, User= and Group= ensure the consumer doesn't accidentally run as root, and WorkingDirectory= sets the working directory correctly before the command is executed.

After creating a new unit file, the order is always the same: daemon-reload, then enable --now. For consumers that might crash occasionally, for example due to an out-of-memory kill by the kernel, Restart=always combined with RestartSec=5 provides automatic recovery without requiring an extra supervisor tool like Supervisord.


# /etc/systemd/system/magento-consumer-inventory.service
[Unit]
Description=Magento Inventory Reservation Queue Consumer
After=network.target mysql.service rabbitmq-server.service
Wants=rabbitmq-server.service

[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/var/www/magento
ExecStart=/usr/bin/php bin/magento queue:consumers:start inventory.reservation.updateSalabilityAfterOrderPlacement --max-messages=1000
Restart=always
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=5
TimeoutStopSec=30
StandardOutput=journal
StandardError=journal
SyslogIdentifier=magento-consumer-inventory

[Install]
WantedBy=multi-user.target

For automation and CI/CD it helps to know that a unit's state can also be read in a machine-readable way: systemctl show magento-consumer-inventory.service --output=json -p ExecMainStatus -p Result -p ActiveState -p SubState -p MainPID returns exactly those fields as a JSON object, which plugs directly into monitoring scripts or deploy pipelines without having to parse the textual status output.


# Ansible task: deploy a systemd unit and ensure its target state
- name: Deploy magento consumer unit file
  ansible.builtin.template:
    src: magento-consumer-inventory.service.j2
    dest: /etc/systemd/system/magento-consumer-inventory.service
    mode: "0644"
  notify: reload systemd

- name: Ensure consumer service is enabled and running
  ansible.builtin.systemd:
    name: magento-consumer-inventory.service
    enabled: true
    state: started
    daemon_reload: true

handlers:
  - name: reload systemd
    ansible.builtin.systemd:
      daemon_reload: true

Mironsoft

Server administration, deployment automation and systemd setups

Reliable systemd services for your stack?

We set up unit files for queue consumers, cron jobs and background processes, configure restart policies and dependencies correctly, and put monitoring in place that reports failed states immediately.

Unit setup

Robust unit files for consumers, workers and cron jobs

Monitoring

Alerting on failed states via journalctl and external tools

Deploy automation

Ansible roles for systemd deployments in CI/CD pipelines

9. systemctl versus SysVinit at a glance

Anyone moving from classic init.d scripts to systemd usually looks for the direct equivalent of a familiar command. The table below maps the most important old patterns to their modern systemctl equivalents.

Task Old way (SysVinit) systemd equivalent Benefit
Start a service service apache2 start systemctl start apache2 Uniform interface for every service
Enable autostart update-rc.d apache2 defaults systemctl enable apache2 Symlink-based, instantly traceable
Inspect logs tail -f /var/log/apache2/error.log journalctl -u apache2 -f Structured, with metadata and filters
Check status ps aux | grep apache2 systemctl status apache2 Shows PID, memory, state and logs
Automatic restart Custom watchdog script via cron Restart=on-failure in the unit file No extra tooling needed

The switch pays off mainly because systemd delivers a consistent view of every service, regardless of whether it is a package-installed web server or a hand-written unit for a Magento consumer. Anyone who masters systemctl and journalctl no longer needs additional tooling for the vast majority of diagnostic and operational tasks.

10. Summary

Managing systemd services boils down to a handful of clearly defined commands: systemctl start/stop/restart for the running state, systemctl enable/disable for autostart, and systemctl status as the first point of contact for every incident. The status output already provides most of the information needed for an initial diagnosis through Active:, Loaded: and Main PID:, without requiring extra tools. journalctl -u adds filtered, structured logs that can be followed live, narrowed by time, and filtered by priority.

Anyone who correctly interprets the meaning of the states failed, activating and dead, and writes custom unit files with clean restart policies and dependencies, replaces fragile cron or nohup constructs with production-grade processes supervised by the kernel. systemctl --failed as a server-wide overview and daemon-reload after every unit change are among the commands that no admin's daily routine can do without.

Managing systemd Services, the essentials at a glance

Core commands

systemctl start/stop/restart for the running state, enable --now for autostart plus an immediate start in a single step.

Reading status

Active:, Loaded: and Main PID: deliver the central diagnosis directly in the status output.

Logs with journalctl

journalctl -u service -f to follow live, -p err and --since for targeted filtering.

States & policies

systemctl --failed as a server-wide overview, Restart=on-failure for automatic recovery.

11. FAQ: Managing systemd Services

1Difference between restart and reload?
restart terminates the process and starts it again (brief downtime). reload signals the running process to reload its configuration without terminating it.
2Why doesn't my service start on boot?
Running state and autostart are separate. systemctl start only starts it now, systemctl enable or enable --now additionally activates autostart.
3What does the failed state mean?
The service was started but exited with an error code, or an ExecStart command failed. It persists until reset-failed or a successful restart.
4How do I find every failing service?
systemctl --failed lists every unit in the failed state across the server. The fastest first command during a general outage report.
5Why daemon-reload after a unit change?
systemd keeps a cached version in memory. Without daemon-reload it keeps operating on the old definition.
6How do I filter journalctl by time range?
With --since and --until, relative ("1 hour ago") or absolute as a timestamp. Precise narrowing without manual grep.
7Difference between Requires and Wants?
Requires is a hard dependency, a failure also stops the dependent unit. Wants is softer: a failure does not prevent the unit's own start.
8How does a crashed consumer restart automatically?
Restart=always or on-failure with RestartSec. StartLimitBurst and StartLimitIntervalSec prevent an infinite restart loop.
9Where do custom unit files belong?
In /etc/systemd/system/. /lib/systemd/system/ is reserved for the package manager and gets overwritten during updates.
10Process journalctl output in a structured way?
journalctl -o json delivers every line as a JSON object with metadata, ideal for log aggregators like Fluentd or Logstash.