Skip to content

Fix: sudo: command not found on Linux

FixDevs · (Updated: )

Part of:  Docker, DevOps & Infrastructure

Quick Answer

How to fix 'sudo: command not found' on Linux — caused by sudo not installed, missing PATH in scripts, Docker containers without sudo, and su vs sudo confusion on minimal systems.

The Error

You run a command with sudo on a Linux system and get:

bash: sudo: command not found

Or:

sudo: command not found

Or in a shell script:

./setup.sh: line 5: sudo: command not found

The system cannot find the sudo binary. This is different from a permissions error like Permission denied or sudo: user is not in the sudoers file — those mean sudo was found but access was denied.

Why This Happens

sudo is not installed on every Linux system by default. It is a separate package that distributions can include, exclude, or replace. On a desktop install of Ubuntu or Fedora you almost always have it. On a server install, a container base image, or a cloud-provider golden image, you often do not. The shell’s command not found message is the kernel-agnostic way of saying that no binary named sudo exists in any directory listed in your current $PATH.

Three categories of root cause cover almost every report. First, the binary is genuinely missing — the package was never installed (typical for Alpine, distroless, busybox, Debian slim, and many CI base images). Second, the binary is installed but $PATH does not include /usr/bin — common inside cron, inside non-login SSH commands, and inside su (without the dash) where the parent shell’s environment is inherited unchanged. Third, you are running on a distribution that ships doas, pkexec, or run0 instead of sudo, and your scripts assume sudo is the only choice.

It is also worth distinguishing this from related failures. Permission denied and user is not in the sudoers file both mean sudo was found and executed — it just refused you. bash: sudo: command not found is strictly a PATH / installation issue and never a permissions one. If you fix the wrong layer (for example, editing /etc/sudoers when the binary is simply not installed) nothing will change.

Common situations where sudo is missing or unreachable:

  • Minimal Docker containers (Alpine, Debian slim, Ubuntu minimal, distroless, busybox) do not include sudo — it is not needed since the container runs as root.
  • Freshly provisioned Debian/Ubuntu servers from some cloud providers or VPS hosts ship without sudo.
  • Alpine Linux uses doas instead of sudo in many configurations.
  • Shell scripts run with su — when you switch to root with su, the PATH may not include /usr/bin where sudo lives.
  • Non-interactive SSH sessions — the PATH set up by .bashrc or .bash_profile may not apply.
  • Docker RUN commands — each RUN is executed as a new shell with a minimal environment.

Platform and Environment Differences

The same sudo: command not found message has very different root causes depending on where you are running. Knowing the platform shortcuts the fix.

Debian and Ubuntu ship sudo as a separate package in the main archive. Server ISOs and the official Docker ubuntu:* images install it, but debian:*-slim and ubuntu:*-minimal images do not. The package is just sudo; an sudo-ldap package also exists for sites that authenticate sudoers through LDAP, and the two conflict — installing one removes the other.

RHEL, CentOS Stream, Rocky, AlmaLinux, and Fedora ship sudo by default on most installs, but the privileged group is wheel, not sudo. Adding a user to a group called sudo on these distros silently does nothing because /etc/sudoers only references %wheel. The same applies to most container images derived from ubi, rockylinux, or fedora.

Alpine Linux and other musl-based distros historically pushed doas (from OpenBSD) over sudo. sudo is available via apk add sudo, but the package adds significant size; for one-off privilege escalation doas is usually preferred. Alpine also enforces stricter group rules — adding to wheel is required, plain sudo group will not work without editing /etc/sudoers.

Distroless and scratch images (gcr.io/distroless/*, scratch) intentionally have no shell at all, let alone sudo. The fix is never to add sudo — it is to redesign the build so privileged work happens in an earlier stage that does have a shell, and the final image only carries the application binary. Trying to install sudo into distroless defeats its security posture.

WSL2 runs a normal Linux user-mode environment, so sudo installs the same way as on bare-metal Ubuntu or Debian. The catch is that WSL distributions imported via wsl --import from a rootfs tarball sometimes drop sudo and you have to reinstall it. Also, the default user in WSL is root when launched via wsl -dsudo is unnecessary there.

Docker containers running as root (the default) never need sudo. The “fix” inside a RUN step is almost always to remove the sudo prefix, not to install the package. Only add sudo when the runtime user is non-root and your application code calls sudo itself.

CI runners vary widely. GitHub Actions Ubuntu runners run as the runner user with passwordless sudo already configured. GitLab CI shared runners using docker:* images run as root and have no sudo. Self-hosted runners reflect whatever image their administrator built. Always probe with whoami && which sudo before assuming.

Linux capabilities can replace sudo entirely for narrow use cases. If your script only needs CAP_NET_BIND_SERVICE (to bind port 80 as a non-root process) or CAP_NET_RAW (for ping), use setcap instead of installing sudo. This is the right approach in unprivileged containers where adding sudo is impossible.

Fix 1: Install sudo

If sudo is simply not installed, install it as root:

Debian / Ubuntu:

# Switch to root first
su -

# Install sudo
apt-get update && apt-get install -y sudo

# Add your user to the sudo group
usermod -aG sudo your-username

# Exit root shell
exit

# Apply the group change (log out and back in, or use newgrp)
newgrp sudo

CentOS / RHEL / Fedora:

su -
dnf install -y sudo        # RHEL 8+ / Fedora
# or
yum install -y sudo        # RHEL 7

usermod -aG wheel your-username
exit

On RHEL/CentOS, the wheel group has sudo access by default (not the sudo group).

Alpine Linux:

# As root
apk add --no-cache sudo

# Add user to wheel group
adduser your-username wheel

# Allow wheel group to use sudo without password (optional)
echo "%wheel ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

After installing and adding your user to the appropriate group, log out and log back in for the group change to take effect. Group membership is read at login — newgrp sudo applies it to the current session without re-logging.

Fix 2: Fix sudo in Docker Containers

Docker containers often run as root, so sudo is unnecessary and not installed. But if your Dockerfile or scripts use sudo, add it:

Dockerfile — Debian/Ubuntu base:

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    sudo \
    && rm -rf /var/lib/apt/lists/*

# Create a non-root user with sudo access
RUN useradd -m -s /bin/bash appuser \
    && echo "appuser ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

USER appuser

Better approach — run as root during build, switch to non-root for runtime:

FROM node:20-slim

# Run build steps as root (no sudo needed)
RUN apt-get update && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*

# Switch to non-root user for the application
RUN useradd -m -u 1001 appuser
USER appuser

WORKDIR /app
COPY --chown=appuser:appuser . .
RUN npm install
CMD ["node", "server.js"]

Pro Tip: In Docker, you do not need sudo in RUN commands — they run as root by default (before any USER instruction). Add sudo only if your application code or scripts explicitly call it at runtime as a non-root user.

Fix 3: Fix sudo in Shell Scripts

When a script is run via su, cron, or a non-login shell, the PATH may not include /usr/bin:

Check where sudo is installed:

which sudo
# /usr/bin/sudo  (most systems)

ls -la /usr/bin/sudo

Use the full path in scripts:

#!/bin/bash
/usr/bin/sudo apt-get update
/usr/bin/sudo systemctl restart nginx

Or fix the PATH at the top of the script:

#!/bin/bash
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

sudo apt-get update

For cron jobs, always use full paths or set PATH explicitly because cron runs with a minimal environment:

# In crontab (crontab -e):
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 2 * * * /usr/bin/sudo /usr/bin/apt-get update -y

Fix 4: Use su Instead of sudo on Systems Without sudo

On minimal systems or older distributions where sudo is not installed and you cannot install it, use su to switch to root:

# Switch to root
su -

# Run your commands as root
apt-get update
systemctl restart nginx

# Return to your user
exit

The su - (with the dash) starts a login shell as root, which sets up the correct PATH and environment. su without the dash inherits your current environment, which may have PATH issues.

Run a single command as root with su -c:

su -c "apt-get update && apt-get install -y curl" root

This is equivalent to sudo apt-get update && sudo apt-get install -y curl.

Fix 5: Fix sudo Not Found in SSH Commands

When running commands over SSH non-interactively, the PATH is often minimal:

Broken — PATH does not include /usr/bin:

ssh user@server "sudo systemctl restart nginx"
# bash: sudo: command not found

Fixed — use the full path:

ssh user@server "/usr/bin/sudo /bin/systemctl restart nginx"

Fixed — force a login shell:

ssh user@server "bash -l -c 'sudo systemctl restart nginx'"

The -l flag starts a login shell which sources .bash_profile and sets up the full PATH.

Fixed — use sudo -i or sudo -s in the remote command:

If the user has passwordless sudo configured, you can also use:

ssh user@server "sudo -n systemctl restart nginx"

The -n flag makes sudo non-interactive — it fails immediately if a password would be required instead of hanging.

Fix 6: Fix sudo in CI/CD Pipelines

In GitHub Actions, GitLab CI, and similar platforms, runners often run as root or a restricted user:

GitHub Actions — check the runner user:

- name: Check user
  run: whoami && id

Most GitHub Actions runners run as a non-root user with sudo available and passwordless. If sudo is not found, install it in your workflow:

- name: Install sudo
  run: |
    if ! command -v sudo &> /dev/null; then
      apt-get update && apt-get install -y sudo
    fi

For Docker-based CI runners, add sudo to your base image as shown in Fix 2.

GitLab CI — use before_script to install dependencies:

before_script:
  - apt-get update -qq && apt-get install -y -qq sudo

Fix 7: Verify sudo Is Configured Correctly After Install

After installing sudo, verify it works:

# Check that sudo binary exists
which sudo

# Check your group membership
groups
id

# Test sudo with a safe command
sudo echo "sudo works"

# If prompted for password and you want passwordless sudo:
sudo visudo

In visudo, add this line at the end to allow your user passwordless sudo:

your-username ALL=(ALL) NOPASSWD: ALL

Or for the entire sudo group:

%sudo ALL=(ALL) NOPASSWD: ALL

Warning: Passwordless sudo is convenient but reduces security. Use it only for automated scripts, CI environments, or development machines — not production servers with sensitive data.

Still Not Working?

Check if sudo is installed but not in PATH:

find / -name "sudo" -type f 2>/dev/null

If it finds /usr/bin/sudo but which sudo returns nothing, your PATH is missing /usr/bin. Add it:

export PATH="$PATH:/usr/bin"

Add this to ~/.bashrc or ~/.bash_profile to make it permanent.

Check the sudoers file for syntax errors:

sudo visudo -c  # Check syntax without opening the editor

A syntax error in /etc/sudoers can break sudo for all users. If you cannot use sudo at all, boot into single-user mode or use pkexec visudo to fix the sudoers file.

WSL (Windows Subsystem for Linux): If you are on WSL and sudo is not found, your WSL distribution may be in a broken state. Try resetting it or reinstalling the distribution from the Microsoft Store. WSL distributions imported from a tar rootfs sometimes ship without sudo — install it via the appropriate package manager once you have switched to root with wsl -d <distro> -u root.

Check whether the distro replaced sudo with doas or run0. On modern Alpine and on some hardened systems, doas is the default. Try which doas and which run0. If one exists, rewrite the script to use it, or apk add sudo / dnf install sudo if you need cross-distro consistency. systemd 256+ introduced run0 as a sudo replacement on bleeding-edge Fedora and Arch.

Check for missing PAM modules. On some hardened images, sudo is installed but fails at startup because /etc/pam.d/sudo references a module that is not present (commonly pam_systemd.so in non-systemd containers). The error then masquerades as sudo: command not found when invoked via a wrapper that swallows stderr. Run sudo -V and look for missing-module warnings, or check journalctl -u sudo if systemd is available.

Check pam_wheel and group membership requirements. Some distributions (notably Arch, Gentoo, and hardened RHEL derivatives) enable auth required pam_wheel.so use_uid in /etc/pam.d/su. If a script falls back to su instead of sudo, that script silently fails unless the user is in wheel. Add the user with usermod -aG wheel <user> and re-login.

Check the busybox shim. On busybox-based images (Alpine, OpenWrt), sudo may be a thin applet that does almost nothing. Run ls -la $(which sudo) and if it points to /bin/busybox, install the real sudo package — the busybox applet does not honor /etc/sudoers.

For permission errors after sudo is working (e.g., sudo: user is not in the sudoers file), the fix is adding the user to the sudo/wheel group as shown in Fix 1. For script permission errors, see Fix: bash: Permission Denied. For Docker socket access without sudo, see Fix: Docker permission denied while trying to connect to the Docker daemon socket. For SSH command execution that loses your interactive PATH, see Fix: SSH Connection Timed Out. For GitHub Actions where sudo suddenly stops being available, see Fix: GitHub Actions Permission Denied.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles