Linux Service Management

systemctlvsservicevsinit.d

Historical background

Linux service management has gone through two distinct eras. For most of Linux's history, the system used SysV init (System V init) — a port of the original Unix init process. Under SysV, every service was a shell script stored in /etc/init.d/. Boot proceeded sequentially: scripts ran one after another, in numbered order.

In 2010, systemd was introduced and has since become the default init system on Debian, Ubuntu, Fedora, RHEL, Arch, and most other major distributions. systemd runs services in parallel, tracks them with cgroups, and uses declarative unit files instead of shell scripts.

The result: three different tools exist because they map to different layers of this evolution. They're not equivalent — two of them are wrappers or legacy interfaces for the third.

init.d scripts

Scripts in /etc/init.d/ are plain shell scripts that accept a verb as their first argument: start, stop, restart, status, reload. They are the raw SysV interface.

# Direct execution — works on any SysV system
/etc/init.d/nginx start
/etc/init.d/nginx stop
/etc/init.d/nginx restart
/etc/init.d/nginx status
NOTE On systemd systems, scripts in /etc/init.d/ may still exist as compatibility shims. Calling them directly invokes a compatibility layer that ultimately delegates to systemd.

Enable at boot (SysV)

# Enable / disable via update-rc.d (Debian/Ubuntu)
update-rc.d nginx enable
update-rc.d nginx disable

# Or via chkconfig (RHEL/CentOS pre-7)
chkconfig nginx on
chkconfig nginx off

Limitations

init.d scripts have no dependency resolution (beyond ordered numbering), no parallel execution, no automatic restart on failure, and no unified logging. You get whatever the script author implemented — which varies widely.

AVOID ON MODERN SYSTEMS Calling /etc/init.d/ scripts directly on a systemd system bypasses systemd's cgroup tracking. The process exists but systemd doesn't know about it, which breaks status, automatic restarts, and log aggregation.

The service command

service is a wrapper shipped on Debian/Ubuntu systems. It abstracts over whichever init system is running. On a SysV system it calls the init.d script directly. On a systemd system it calls systemctl.

service nginx start
service nginx stop
service nginx restart
service nginx status

It is intentionally minimal: it covers only the four runtime operations above. It cannot enable services at boot, mask them, show logs, list units, or interact with systemd targets. That is not an oversight — it was designed for portability, not power.

What it actually does on systemd

# service nginx start  →  translates to:
systemctl start nginx.service

# You can verify this by running:
which service              # /usr/sbin/service
cat /usr/sbin/service      # it's a shell script
TIP service is useful in scripts that must run on both SysV and systemd hosts. For interactive use on modern systems, systemctl gives you more information and control.

systemctl

systemctl is the primary command-line interface to systemd. It manages units — not just services (.service), but also sockets (.socket), timers (.timer), mount points (.mount), and more. Everything below focuses on services.

Runtime control

systemctl start   nginx
systemctl stop    nginx
systemctl restart nginx
systemctl reload  nginx   # sends SIGHUP; fails if not supported
systemctl status  nginx   # shows state, cgroup, last log lines

Boot persistence

systemctl enable  nginx   # creates symlink; starts at boot
systemctl disable nginx   # removes symlink

# Enable AND start in one command
systemctl enable --now nginx

# Disable AND stop in one command
systemctl disable --now nginx

Masking (hard disable)

# Masking prevents a service from being started by anything,
# including dependencies or other services.
systemctl mask   bluetooth
systemctl unmask bluetooth

Introspection

# Is it running right now?
systemctl is-active  nginx

# Is it enabled at boot?
systemctl is-enabled nginx

# Is it masked?
systemctl is-masked  nginx

# Show all running services
systemctl list-units --type=service --state=running

# Show failed units
systemctl --failed

# Dump the full unit file
systemctl cat nginx

Logs via journalctl

# Follow logs for a specific service
journalctl -u nginx -f

# Last 50 lines
journalctl -u nginx -n 50

# Since last boot
journalctl -u nginx -b

Reload systemd after editing a unit file

# After editing /etc/systemd/system/nginx.service
systemctl daemon-reload
systemctl restart nginx

Side-by-side comparison

Feature init.d script service systemctl
Start / stop / restart
Enable at boot update-rc.d / chkconfig ✓ native
Mask (hard disable)
Integrated logs ✗ (syslog only) ✓ via journalctl
Parallel startup
Dependency management ordering by number only ✓ full DAG
Auto-restart on failure ✗ (manual in script) ✓ Restart= directive
Cgroup process tracking
Portable across init systems SysV only ✓ abstraction layer systemd only
List all services + status

Command cheat sheet

Intent SysV / init.d systemctl (modern)
Start a service /etc/init.d/nginx start systemctl start nginx
Stop a service /etc/init.d/nginx stop systemctl stop nginx
Restart a service /etc/init.d/nginx restart systemctl restart nginx
Check status /etc/init.d/nginx status systemctl status nginx
Enable at boot update-rc.d nginx enable systemctl enable nginx
Disable at boot update-rc.d nginx disable systemctl disable nginx
Start + enable two commands systemctl enable --now nginx
View logs cat /var/log/nginx/error.log journalctl -u nginx -f
Reload unit files n/a systemctl daemon-reload
List failed n/a systemctl --failed

Which one to use

The answer depends on your context:

On any modern distro (Ubuntu 16+, Debian 8+, RHEL/CentOS 7+, Fedora 15+)

Use systemctl for everything. It is the authoritative interface. service works for start/stop/restart but gives you no visibility into the system. init.d scripts should not be called directly.

In portable shell scripts

If your script must run on both systemd and SysV hosts, service is reasonable for the four basic operations. Add a check at the top of the script:

if command -v systemctl >/dev/null 2>&1; then
  SVCMGR="systemctl"
else
  SVCMGR="service"
fi

$SVCMGR nginx restart

On legacy systems (CentOS 6, Debian 7, or older)

No systemd is present. Use /etc/init.d/ scripts directly or via service. Enable at boot via update-rc.d (Debian) or chkconfig (RHEL).

QUICK RULE If systemctl exists on the host, use it. Run which systemctl to check.

Compatibility layer

On systemd hosts, init.d scripts are still supported through a compatibility shim. When you run /etc/init.d/nginx start, the script detects systemd is running and delegates to systemctl start nginx via /lib/lsb/init-functions.

Similarly, service nginx start on a systemd host checks for the corresponding .service unit first; if found, it calls systemctl. If no unit is found but an init.d script exists, it falls back to that.

This means calling any of the three tools on a modern system ultimately reaches systemd — but only systemctl gives you the full status picture, journal integration, and dependency graph. The others are convenience wrappers with deliberately limited scope.

# Check what's actually managing a service on your system:
systemctl show nginx --property=FragmentPath
# FragmentPath=/lib/systemd/system/nginx.service  → native unit
# FragmentPath=/run/systemd/generator.late/...    → generated from init.d
NOTE A service with a generated unit (from an init.d script) has fewer capabilities than a native .service unit. Features like Restart=on-failure, After= dependencies, and Type=notify require a real unit file in /etc/systemd/system/ or /lib/systemd/system/.