Short answer: systemctl: command not found almost always means the machine is not running systemd: a Docker container, Alpine Linux with OpenRC, WSL without systemd switched on, an older SysVinit system, or macOS. The fix is not to install systemctl; it is to use the service manager the machine actually has. One line tells you which:
cat /proc/1/commsystemd means systemctl should work (see the PATH case). Anything else: run the program directly in a container, use rc-service on Alpine, service on SysVinit, systemd=true in /etc/wsl.conf on WSL, and brew services or launchctl on a Mac.
Which error do you see?
Two families of messages exist. Either the shell finds no systemctl at all, or systemctl is installed and finds no systemd to talk to. The second pair below is real output of Debian 12’s systemctl (systemd 252) on a system where systemd is not running; the newer wording is from systemd 257 in Debian 13 and is unchanged in 259, which Ubuntu 26.04 ships.
| Message | Where you see it | Go to |
|---|---|---|
bash: systemctl: command not found | no systemd installed: container, Alpine with bash, SysVinit | Which init |
/bin/sh: 1: systemctl: not found | dash (Debian, Ubuntu) in a Dockerfile RUN line or a cron job; a #!/bin/sh script prints its own name and line instead, as in ./setup.sh: 3: systemctl: not found | Docker |
/bin/sh: systemctl: not found | the same on Alpine (BusyBox); a login shell prints -sh: | Alpine |
zsh: command not found: systemctl | macOS, which has launchd, not systemd | macOS |
System has not been booted with systemd as init system (PID 1). Can't operate. | systemctl installed, PID 1 is not systemd: container or WSL | Docker |
Failed to connect to bus: Host is down | second line of the above, systemd 256 and older (Debian 12, Ubuntu 24.04) | WSL |
Failed to connect to system scope bus via local transport: Host is down | the same second line from systemd 257 on (Debian 13; Ubuntu 26.04 has 259) | WSL |
You are attempting to run an openrc service on a | rc-service in an Alpine container OpenRC did not boot | Alpine |
Failed to connect to bus: No medium found | systemctl --user after su or sudo -u, systemd 250 to 256 (Debian 12, Ubuntu 24.04) | User services |
Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined | the same on systemd 249 (Ubuntu 22.04) | User services |
Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined | the same from systemd 257 on (Debian 13, Ubuntu 26.04) | User services |
First, find out what init you have
cat /proc/1/comm # prints: systemd, init, bash, node, ...
ps -p 1 -o comm=ps is often missing in containers; /proc/1/comm always works. Read the answer like this:
systemd: systemd is running, and systemctl should exist. Go to the PATH case.init: one of three systems. Alpine (BusyBox init, which starts OpenRC), SysVinit (Devuan, MX Linux, Debian before version 8), or an older WSL running its own init. The next block tells them apart.init(Ubuntu),init(Debian)or another distribution name in brackets: current WSL, whose own init names itself that way. Go to WSL.- Anything else, such as
bash,sh,node,nginx,tiniordocker-init: you are in a container whose first process is the application. Go to Docker.
grep -i microsoft /proc/version # any output: WSL
command -v rc-service # a path: OpenRC (Alpine)
grep PRETTY_NAME /etc/os-releaseIn a Docker container
A container runs one process tree, and its PID 1 is your application or a tiny init such as tini, not systemd. The official debian, ubuntu and alpine images do not even include systemctl. If a package pulled systemd in, it is there but refuses:
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is downDo not try to make systemctl work. Start the program in the foreground, which is what the official images do:
CMD ["nginx", "-g", "daemon off;"]Inside a running container, call the program itself: nginx -s reload instead of systemctl reload nginx. On Debian and Ubuntu images, service SERVICE start still works for packages that ship a script in /etc/init.d/: the service wrapper hands over to systemctl only when /run/systemd/system exists and otherwise runs the script. Without a script it answers SERVICE: unrecognized service.
On WSL
WSL starts each distribution with its own init unless told otherwise. PID 1 is then WSL’s init, which current versions name after the distribution: cat /proc/1/comm prints init(Ubuntu), older ones plain init. systemctl is then either missing or prints the two lines above. Ubuntu installed with wsl --install has systemd on by default; for other distributions, such as Debian, switch it on per distribution. It needs WSL 2 and WSL version 0.67.6 or newer (wsl --version shows it; wsl --update updates it). On Debian, Ubuntu and Kali, Microsoft’s guide also asks for the systemd and systemd-sysv packages; install them before the switch:
sudo apt-get update && sudo apt-get install -y systemd systemd-sysvThen add this to /etc/wsl.conf inside the distribution (with sudo nano /etc/wsl.conf):
[boot]
systemd=trueRestart WSL from Windows:
wsl.exe --shutdownOpen the distribution again and cat /proc/1/comm prints systemd. A distribution still on WSL 1 has no systemd support; convert it with wsl --set-version DISTRO 2.
Alpine (OpenRC)
A full Alpine install boots BusyBox init, which starts OpenRC. Its tools are rc-service, rc-update and rc-status:
rc-service SERVICE start
rc-service SERVICE status
rc-update add SERVICE defaultThe last line is the equivalent of systemctl enable. In an Alpine container, OpenRC is usually not installed at all, and where it is, it did not boot the system: rc-service then prints a warning that begins You are attempting to run an openrc service on a and continues system which openrc did not boot. Run the program directly, as in Docker.
SysVinit
Devuan and MX Linux use SysVinit by default, and so did Debian before version 8. Services are scripts in /etc/init.d/, run through service:
service SERVICE start
service SERVICE status
update-rc.d SERVICE defaultsupdate-rc.d SERVICE defaults enables a service at boot on Debian-style systems; update-rc.d SERVICE disable turns that off again.
systemctl commands and their equivalents
| Task | systemd | OpenRC (Alpine) | SysVinit (Debian-style) | macOS (Homebrew) |
|---|---|---|---|---|
| Start | systemctl start SERVICE | rc-service SERVICE start | service SERVICE start | brew services start FORMULA |
| Stop | systemctl stop SERVICE | rc-service SERVICE stop | service SERVICE stop | brew services stop FORMULA |
| Restart | systemctl restart SERVICE | rc-service SERVICE restart | service SERVICE restart | brew services restart FORMULA |
| Status | systemctl status SERVICE | rc-service SERVICE status | service SERVICE status | brew services info FORMULA |
| Start at boot | systemctl enable SERVICE | rc-update add SERVICE default | update-rc.d SERVICE defaults | start registers it |
| Stop starting at boot | systemctl disable SERVICE | rc-update del SERVICE default | update-rc.d SERVICE disable | stop unregisters it |
| List services | systemctl list-units --type=service | rc-status | service --status-all | brew services list |
macOS: launchd instead of systemd
macOS has never had systemd; its service manager is launchd, driven by launchctl. For anything installed with Homebrew, brew services is the practical front end: it writes the launchd file for you. Without sudo it manages ~/Library/LaunchAgents, started at login; with sudo, /Library/LaunchDaemons, started at boot. launchctl list shows what launchd is running for your user.
If you do have systemd but the command is missing
systemctl lives in /usr/bin (also reachable as /bin/systemctl where /bin links to /usr/bin), which every default PATH includes, and sudo’s secure_path does too. So with systemd as PID 1, “not found” means PATH was overwritten, usually by a startup file that sets PATH without $PATH:
ls -l /usr/bin/systemctl
/usr/bin/systemctl status
echo "$PATH"Restore PATH with export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH" and fix the line in ~/.bashrc or ~/.profile.
systemctl --user: Failed to connect to bus
systemctl --user talks to the user’s own service manager, and it finds that through the session a real login creates. After su or sudo -u that session is missing. systemd 250 to 256, as in Debian 12 and Ubuntu 24.04, then says only Failed to connect to bus: No medium found. Ubuntu 22.04 (systemd 249) prints Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user), and systemd 257 and later print Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user). Log in as that user over SSH, or address the user manager from root:
systemctl --user --machine=USERNAME@.host statusThe user manager runs only while the user is logged in. For user services that must run without a login, enable lingering once: loginctl enable-linger USERNAME.
Verify it worked
systemctl is-system-runningrunning means systemd is in charge and healthy; degraded means it is in charge but a unit failed (systemctl --failed lists them). offline means systemctl is installed but systemd is not PID 1, which is what you get in a container or in WSL before the change above.
A full KVM VPS runs a normal systemd userland, so service management works exactly as the documentation expects. This is also why bots belong on a full VM: a Discord or Telegram bot under systemd restarts itself after a crash and at boot. Our Standard KVM line ships stock systemd images of Debian, Ubuntu, Rocky and more.
Writing a unit for your own program next? See how to create a systemd service.
Another tool missing, or a directory to add to PATH for good in bash, zsh and fish? See command not found: the three causes and the fix for every tool.
Frequently asked
What does 'System has not been booted with systemd as init system (PID 1)' mean?
systemctl is installed but systemd is not running as PID 1, typically in a Docker container or in WSL without systemd. In a container, start the program directly or use 'service NAME start' where the package ships an init script; on WSL, enable systemd in /etc/wsl.conf.
How do I get systemctl working in WSL?
Add '[boot]' and 'systemd=true' to /etc/wsl.conf inside the distribution, run 'wsl.exe --shutdown' from PowerShell and open it again. It needs WSL 2 and WSL 0.67.6 or newer, and on Debian the systemd and systemd-sysv packages; Ubuntu installed with 'wsl --install' already has it on.
What replaces systemctl on Alpine or macOS?
Alpine uses OpenRC: 'rc-service NAME start' and 'rc-update add NAME default' to start at boot. macOS uses launchd; for software from Homebrew, 'brew services start NAME' starts it and registers it to start at login.