Troubleshooting

ps: command not found — install procps (and get top, free and pkill back)

Slim Docker images leave out procps, so ps, top, free, pgrep and pkill vanish together. Install it on Debian, Ubuntu, Rocky or Alpine, or read /proc.

Published ~7 min read

ps: command not found means the procps package is not installed. That happens mostly in containers, because slim base images leave it out. The same package provides top, free, pgrep, pkill, uptime, vmstat and watch, so they all disappear together, and one install brings them all back: procps on Debian and Ubuntu, procps-ng on Rocky, Alma and Alpine.

quick fix
# Debian, Ubuntu, and images built on them (node:*-slim, python:*-slim)
apt-get update && apt-get install -y procps

# Rocky, AlmaLinux, RHEL, Fedora
dnf install -y procps-ng
# Red Hat UBI minimal images
microdnf install -y procps-ng

# Alpine: replaces BusyBox's ps with the full version
apk add --no-cache procps-ng

ps --version

Inside a container you are usually root already, so the commands have no sudo. On a normal server, put sudo in front.

Why is ps missing?

ps is not built into the shell and is not part of coreutils. It belongs to procps, a package that every regular Debian, Ubuntu or Rocky server install includes, but that container images skip to save space. The package lists of the official images show where you will run into it:

  • No procps: debian:12 and debian:13 (the slim tags and the regular ones), node:*-slim, python:*-slim and Red Hat’s ubi9/ubi-minimal.
  • procps included: ubuntu:22.04 and ubuntu:24.04, and the full node and python images, which are built on a larger Debian base.
  • BusyBox ps: alpine and images built on it. ps exists there but understands only a few options. See the Alpine section.

On Debian 12 the package contains ps, top, free, pgrep, pkill, pidwait, pmap, pwdx, uptime, vmstat, w, watch, sysctl, kill and a few smaller tools. If any of those is “not found”, the fix is the same.

How do I install procps on each distribution?

Debian and Ubuntu

Debian · Ubuntu
sudo apt update
sudo apt install -y procps

Rocky, AlmaLinux, RHEL and Fedora

Rocky · Alma · RHEL · Fedora
sudo dnf install -y procps-ng

# ubi-minimal and other images that only have microdnf
microdnf install -y procps-ng && microdnf clean all

Alpine

Alpine
apk add --no-cache procps-ng

Arch and openSUSE

Arch · openSUSE
sudo pacman -S --needed procps-ng     # Arch, Manjaro
sudo zypper install -y procps         # openSUSE

Check the result with ps --version. It should print a line starting with ps from procps-ng.

Why do ps options fail on Alpine?

Alpine’s ps is a BusyBox applet. It prints a simple process list and accepts -o with a short list of columns and -T for threads, and nothing else (a few flags such as -e and -f are accepted and ignored). On current Alpine releases it looks like this; the exact column list depends on how BusyBox was built:

BusyBox ps on Alpine
/ # ps aux                      # accepted, but prints the same short list as plain ps
PID   USER     TIME  COMMAND
/ # ps -p 1
ps: unrecognized option: p
/ # ps -eo pid,comm --sort=-%cpu
ps: unrecognized option: sort=-%cpu
/ # ps -o pid,%cpu,comm
ps: bad -o argument '%cpu', supported arguments: user,group,comm,args,pid,ppid,pgid,etime,nice,rgroup,ruser,time,tty,vsz,sid,stat,rss

-u and --forest fail the same way. When a script or a monitoring tool expects the full ps, run apk add --no-cache procps-ng. That replaces the BusyBox version, along with BusyBox’s top, free and pgrep.

How do I keep ps in a Docker image?

Installing procps by hand in a running container works until the container is recreated, and then it is gone again. Add it to the Dockerfile instead. This matters when something inside the container calls ps on its own: health checks built on pgrep, entrypoint scripts that use pkill, or workflow engines such as Nextflow, whose task wrapper stops with Command 'ps' required by nextflow to collect task metrics cannot be found. For Nextflow, every process container needs it.

Dockerfile · Debian or Ubuntu base
RUN apt-get update \
 && apt-get install -y --no-install-recommends procps \
 && rm -rf /var/lib/apt/lists/*
Dockerfile · UBI minimal · Alpine
# registry.access.redhat.com/ubi9/ubi-minimal
RUN microdnf install -y procps-ng && microdnf clean all

# alpine
RUN apk add --no-cache procps-ng

Once it works, do not be surprised by a short list. A container has its own PID namespace, so ps inside it shows only the container’s processes, and PID 1 is your application or its entrypoint, not systemd. To see everything on the machine, run ps on the host.

It is the same pattern as for any other missing tool in an image. Our wget article uses it too, and ifconfig goes missing from minimal images for the same reason.

How do I list processes without ps?

Everything ps shows comes from /proc, where each running process has a numbered directory. If you can open a shell in the container but cannot install anything, read it directly:

any shell · no procps needed
# PID, process name and full command line
for d in /proc/[0-9]*; do
  printf '%s\t%s\t%s\n' "${d#/proc/}" "$(cat "$d/comm" 2>/dev/null)" \
    "$(tr '\0' ' ' < "$d/cmdline" 2>/dev/null)"
done

# instead of free: memory in kB
grep -E '^(MemTotal|MemAvailable|SwapTotal|SwapFree):' /proc/meminfo

# instead of uptime: load averages for 1, 5 and 15 minutes
cat /proc/loadavg

# instead of pgrep nginx: prints /proc/<PID>/comm for each match
grep -l '^nginx$' /proc/[0-9]*/comm

You do not need procps to stop a process either. kill is built into bash, dash and BusyBox’s sh, so kill <PID> works with a PID from the loop above.

Installed, but still “command not found”?

  • The shell cached the old lookup. Run hash -r, or start a new shell. This happens on Alpine, where the shell may still remember BusyBox’s path.
  • PATH was reset. procps installs to /usr/bin (or /bin). If ls /usr/bin/ps shows the file but command -v ps prints nothing, a script or a profile has overwritten PATH.
  • A script exits with code 127. That is the shell’s status for “command not found”. Find out which command is missing with exit code 127 explained.

Which ps commands do you actually need?

Usually ps was missing because you wanted to find out what is running, or what is using the CPU or the memory. With procps installed, these cover nearly every case:

procps · everyday commands
ps aux                     # every process, BSD style: user, %CPU, %MEM, command
ps -ef                     # every process, System V style, with the parent PID
ps -fp 1234                # one process in detail

ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head    # busiest CPU users first
ps -eo pid,user,%mem,rss,comm --sort=-%mem | head     # biggest memory users first

pgrep -a nginx             # PIDs and command lines that match a name
pkill -f 'node server.js'  # stop processes by their full command line
top -o %MEM                # interactive view, sorted by memory

ps aux and ps -ef show the same processes in two traditional formats; use whichever you can read faster. pkill -f matches the whole command line, so test the pattern with pgrep -af first.

VPS high CPU usage explains how to read the result. If the container turns out to have no systemctl either, that has the same root cause, and systemctl: command not found covers it. If docker itself is missing on the host, see docker: command not found. To check whether a problem is your image or your host, a clean KVM VPS with a stock Debian or Ubuntu install is a cheap place to compare.

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

Which package provides ps?

procps on Debian, Ubuntu and openSUSE; procps-ng on Rocky Linux, AlmaLinux, RHEL, Fedora, Arch and Alpine. The same package also provides top, free, pgrep, pkill, uptime, vmstat, w and watch, which is why they all go missing together.

Why is ps missing from my Docker container?

Many base images leave procps out to stay small, including debian:12 and debian:13, node:*-slim, python:*-slim and Red Hat's ubi-minimal. Install it in the Dockerfile ('apt-get install -y procps', 'dnf install -y procps-ng' or 'microdnf install -y procps-ng') so it survives a rebuild, instead of inside a running container.

How do I list processes without ps?

Read /proc: every running process has a numbered directory there, with its name in 'comm' and its command line in 'cmdline', so a short shell loop over /proc/[0-9]* replaces ps. From the host, 'docker top <container>' lists a container's processes even when the container has no ps.

Is BusyBox ps enough?

For a plain process list, yes. BusyBox ps on Alpine only understands -o with a short list of columns and -T, so options such as -p, -u, --sort, --forest or the %cpu column fail. Install procps-ng when a script or tool expects the full ps.

Related articles

All troubleshooting