pkill Command: Tutorial & Examples

The daemon-administrator's hammer — signal a service by name without ever touching a PID.

What It Is

pkill is how grown-ups signal processes on a modern Linux box. You name what you want — nginx, java.*MyApp, "every child of PID 14021" — and pkill finds the matching PIDs in /proc and delivers a signal to each one. By default that signal is SIGTERM, the polite "please wrap up" — the same default kill sends when you give it a bare PID. pkill just spares you the PID-hunting step.

If you've read the pgrep page you already know the deepest fact about this command: pkill and pgrep are the same tool wearing two name tags. Every flag here works there. That symmetry is the whole point — it's what makes pkill the only safe scriptable kill-by-name on Linux: you preview with pgrep, swap one letter, execute with pkill. And if you've read kill, pkill sends the same signals (-TERM, -HUP, -9, -STOP) over the same kill(2) system call — it's pure sugar on top, and a much better sugar than killall for anything beyond a quick interactive cleanup.

This page is about the second job pkill quietly does better than anything else: daemon administration. pkill -HUP nginx, pkill -P $build_pid, pkill -f 'java.*MyApp' — these are the one-liners that real admins type ten times a day on a box that doesn't run systemd for everything.

Your First Look

Stop every running copy of nginx:

pkill nginx

No output on success — Unix convention, silence means it worked. Every process whose name contains nginx got SIGTERM. If nothing matched, pkill is also silent — but the exit code is 1, so scripts can tell.

Want a receipt? Add -e (echo) to see what was hit:

pkill -e nginx
nginx killed (pid 2341)
nginx killed (pid 2342)
nginx killed (pid 2343)

That's the whole shape of pkill: name in, signals out, three-line vocabulary worth memorising before we get any deeper:

pkill name              # SIGTERM by name (substring match — careful)
pkill -HUP nginx        # reload nginx config without restarting
pkill -9 stuck-thing    # SIGKILL — only after SIGTERM was ignored

How I Use It

The shape of every pkill session is the same three steps, and once they're muscle memory you'll never kill $(pgrep ...) again.

Step 1: preview with pgrep -af. I almost never run pkill without first running the exact same command with pgrep and -a (list-full) instead. Same flags, same pattern. If pgrep -af 'java.*MyApp' shows me the three PIDs and command lines I expected, then pkill -f 'java.*MyApp' is safe. If it shows me seven PIDs — including some I didn't expect — I caught the mistake while I still had a job. The two lines differ by exactly one word, and that one word is the cheapest insurance policy in shell work.

Step 2: pick the right matcher. The default match is substring against the truncated 15-character process name stored in /proc/<PID>/stat — the same kernel limit that bites pgrep. Two flags fix almost every wrong-target problem: -x for exact name match, and -f to match against the full command line (/proc/<PID>/cmdline), which is the only way to single out one of fourteen java processes by what's after java on the command line. pkill ssh is a near-guaranteed lockout (substring matches sshd, ssh-agent, and your live ssh sessions); pkill -x ssh matches only the literal command ssh; pkill -f 'ssh holger@' targets one specific session.

Step 3: pick the right signal. Default is SIGTERM and that's almost always right. The two non-default signals worth typing reflexively are -HUP (daemon, reread your config) and — only after SIGTERM has been ignored for ten seconds — -9 (SIGKILL, the kernel yanks the process out of existence with no cleanup). See kill for the full signal catalogue; everything that works there works here.

Pro Tip

the preview habit is the magic of this command. Type pgrep -af 'pattern' first, read what would be hit, then press up-arrow and change pgrep -af to pkill -f (or pkill -HUP -f, etc.) — same pattern, same flags. pkill and pgrep are the same binary wearing different name tags, which is why the flags line up perfectly. Build this habit and you stop being scared of broad-match kills.

The Flags Explained

Every pkill flag, grouped by what it actually does for you. The catalogue is identical to pgrep; the only differences are output flags (which only make sense on pgrep) and -v (deliberately disabled here so you can't accidentally signal the inverse of your match).

Matching: how the pattern is compared.

  • -f / --full — match against the full command line (/proc/<PID>/cmdline), not the truncated 15-character name. The flag that unlocks targeting one specific Java app among fourteen java processes. The pattern is an extended regex, so pkill -f 'java.*MyApp' works.
  • -x / --exact — exact match, no substring. pkill -x ssh matches only the literal name ssh, leaving sshd and ssh-agent alone.
  • -i / --ignore-case — case-insensitive match.

Filtering: AND-narrow the candidate set. Every filter you add must also match — pkill -u root sshd is "named sshd AND owned by root."

  • -u euid,... / -U uid,... — by effective or real user ID. pkill -u alice (with no name) signals every process alice owns.
  • -g pgid,... / -G gid,... — by process group or real group ID. Group 0 resolves to pkill's own group.
  • -P ppid,... / --parent — match only children of the given parent PID. The cleanest "kill this whole subtree" without invoking shell job control. See Reading by Example.
  • -s sid,... / --session — by session ID.
  • -t tty,... / --terminal — by controlling tty (e.g. -t pts/0). Useful for "kill everything that's still attached to that disconnected serial line."
  • -r state / --runstates — by run state: D, R, S, Z, T, I. (Note: D-state processes can't actually be signaled — see kill Gotchas.)
  • -O secs / --older — only processes older than N seconds.
  • -n / -o — keep only the newest or oldest match. pkill -n -f 'java.*' kills only the youngest matching Java.
  • -A / --ignore-ancestors — ignore pkill's own ancestor chain (shells, sudo). Saves you from a bash script that accidentally matches itself via -f.
  • -F file / --pidfile — read PIDs from a pidfile, the daemon integration. Pair with -L to fail unless the pidfile is locked (proves the daemon is alive).
  • --cgroup name / --ns pid / --nslist — modern systemd world: filter by cgroup v2 or by sharing a namespace with another PID. The bridge from old-school name matching to the container era.

Signal & delivery: what to send and how.

  • -SIGNAL / -s SIGNAL / --signal SIGNAL — send the named signal instead of SIGTERM. pkill -HUP nginx, pkill -USR1 nginx (log rotate), pkill -9 stuck (last resort), pkill -STOP 1234 (freeze). Any signal from kill -l.
  • -e / --echo — print name killed (pid N) for each process signaled. The receipt flag. This is the pkill-only sibling of pgrep's -a.
  • -c / --count — print how many processes matched (still sends the signal). Exit code is non-zero on count of 0.
  • -q value / --queue — use sigqueue(3) instead of kill(2) and attach an integer payload the receiver can read via siginfo_t. The bridge to real-time signals and the most obscure flag here — almost nobody uses it, but if you're writing a daemon that listens for typed messages, this is how you send them.
  • -H / --require-handler — only signal processes that have a userspace handler installed for the signal being sent. Stops you sending SIGUSR1 to a process that would treat it as the default (terminate).

Warning

pkill ssh on a remote box is the classic foot-gun. Substring match grabs sshd (the daemon accepting your future logins), ssh-agent (holding your private keys), and every live ssh session including the one you're typing in. The connection drops mid-keystroke and you can't reconnect because sshd is gone. Always preview with pgrep -af ssh first, or use pkill -x sshd if you really mean only the daemon.

Reading It by Example

The handful of pkill recipes worth real muscle memory.

Preview, then execute — the one-letter swap:

pgrep -af 'java.*MyApp'         # what would I hit?
pkill -f  'java.*MyApp'         # same flags, same pattern, now do it

Read line 1. If it's right, hit up-arrow, change pgrep -af to pkill -f, run. Every time.

Tell a daemon to reload its config without restarting it:

pkill -HUP nginx

SIGHUP is the universal "reread your config file" message across well-behaved Unix daemonsnginx, apache, postfix, bind, haproxy, sshd all honor it the same way. This is the modern form of the kill-page idiom kill -HUP $(cat /var/run/nginx.pid) — same effect, no pidfile path, no cat subshell. On a systemd-managed service systemctl reload nginx is friendlier (and itself just sends SIGHUP under the hood), but on anything else, this one-liner is the standard.

Kill a whole subtree by parent PID — the cleanest cleanup move on a Linux box:

pkill -P 14021

Every direct child of PID 14021 gets SIGTERM. Lovely for a runaway worker pool whose supervisor is misbehaving, a build shell that spawned thirty subprocesses, or a bash script you launched in the background and want to stop without killing the shell itself. Combine with -f and a regex for finer targeting.

Rotate logs without restarting — the logrotate integration pattern:

pkill -USR1 nginx

nginx catches SIGUSR1 and re-opens its log files (so logrotate can move the old one out from under it). Application-specific — check the man page for whatever daemon you're poking — but the convention is broad.

Freeze, inspect, resume — the debugging dance:

pkill -STOP -f 'java.*MyApp'    # freeze the app
strace -p $(pgrep -n -f 'java.*MyApp')   # peek
pkill -CONT -f 'java.*MyApp'    # let it run again

The process is frozen by the kernel, uses zero CPU, and you can attach strace, open /proc, look at file handles with lsof. No state lost.

Clean out a user's session:

pkill -u alice -KILL

No process name — with -u you can omit it entirely. Every process alice owns gets SIGKILL. The "force-logout" admin move.

Cheat Sheet

The lines worth memorising:

  • pgrep -af pattern (preview)pkill -f pattern (execute) — the one-letter swap. The most important habit on this page.
  • pkill -HUP daemon — reload config without restart. The daemon-admin one-liner.
  • pkill -x exact-name — exact-name match, no substring lockouts.
  • pkill -f 'regex' — match against the full command line.
  • pkill -P PPID — kill every child of one parent.
  • pkill -u user — every process owned by USER.
  • pkill -e ... — echo what was killed (the receipt).
  • pkill -9 thingSIGKILL, last resort after SIGTERM was ignored.
  • pkill -F /var/run/foo.pid — signal whatever's in a pidfile.

How You'll Actually Use It

Three contexts. Daemon administration on a server you own: pkill -HUP nginx after editing nginx.conf, pkill -USR1 nginx from your logrotate postrotate hook, pkill -F /run/myapp.pid -TERM from a deploy script. Interactive cleanup: you're at the shell, something is misbehaving, you type pgrep -af pattern, eyeball it, swap to pkill, done. Inside scripts and cron jobs: pkill is the only safe scriptable kill-by-name on Linux, because -f lets you anchor the match to something specific instead of gambling on a 15-character truncation, and the regex grammar lets you be as precise as you need to be.

A word on the modern world: if the thing you're signaling is managed by systemd — and on most distros that's now most daemons — the right tool is systemctl reload nginx, systemctl restart nginx, systemctl kill --signal=HUP nginx. systemd knows the cgroup, so it can't miss a child or hit the wrong process, and it handles the SIGTERM-then-SIGKILL escalation timeout for you. pkill is for everything that isn't a systemd unit: your own scripts, ad-hoc workers, builds that wedged, the Python repl you left running on a dev box, the Docker container's entrypoint that spawned a tree of helpers.

Gotchas

  • The substring-match default is promiscuous. pkill ssh matches sshd, ssh-agent, and every live ssh session — kicking yourself off the box you're working on. Use -x for exact, or -f plus a tighter regex, or always preview with pgrep first.
  • The 15-character truncation. The kernel stores only 15 chars of the process name in /proc/<PID>/stat. pkill my-very-long-daemon matches nothing, but pkill -f my-very-long-daemon works perfectly. When in doubt, use -f.
  • The pattern is an extended regex, not a glob. pkill 'nginx|apache' works. pkill 'nginx*' does not mean what you think it means.
  • pkill -v is deliberately disabled. pgrep accepts -v to invert the match — and that's safe when you're just printing PIDs. On pkill, inverting the match means "signal everything that doesn't match," which is the kind of mistake that ends careers. The maintainers disabled the short flag so you can't reach it by accident. The long form --inverse still works if you're sure.
  • -f matches your own command line too. A bash script that contains the pattern in its source can match itself. Add -A to ignore your own shell chain.
  • You can only signal what you own. Mortals can't signal root's processes; root can signal anyone. pkill returns non-zero silently when nothing was signaled — pair with -e if you want to know.
  • Exit code 1 = "no matches" or "signal failed." Both look the same to a script. If you need to distinguish, use pgrep -c to count first.
  • D-state processes can't be killed. Not even with -9. The process is asleep inside a kernel call (usually stuck disk or frozen NFS) and signals don't deliver until it wakes. Same gotcha as kill.

History & Philosophy

pkill came from Solaris in 1996, alongside its twin pgrep, explicitly to retire the ps | grep | awk | kill pipeline that everybody was typing wrong. POSIX blessed them as the standardised name-based process tools — a fresh pair of names with no historical landmine, deliberately chosen to avoid the BSD-vs-Linux killall name collision (see the killall page for the full graybeard horror story). They landed in the Linux procps package soon after and are now in every distro by default.

The implementation detail worth knowing: pkill and pgrep (and pidwait, the newer "block until matching process exits" sibling) are one binary with three names. The executable inspects argv[0] to decide which mode to run in. Same code, same matching logic, same flag parser — that's why the flags line up perfectly, and it's why the preview-then-execute habit is so cheap to teach: it's literally one program reading the same arguments two different ways.

The deeper magic — same one running through top, ps, pgrep, killall — is that pkill isn't doing anything you couldn't do yourself. It opens /proc, iterates the numeric directories, reads each /proc/<PID>/stat (or cmdline when you pass -f), matches against your regex, then calls the kill(2) system call on every hit. The whole "signal by name" feature is twenty lines of shell over a /proc walk. What pkill adds is the flag vocabulary-P, -u, -F, -x, --cgroup — and the pgrep sister-binary that lets you check your work for free. That combination is the actual feature.

See Also

  • pgrep — the sister command; identical flags, prints PIDs instead of signaling. Always preview here first.
  • kill — the primitive: one PID, one signal. Full signal catalogue lives there.
  • killall — the older kill-by-name; exact-match by default, no preview sibling, BSD foot-gun history
  • ps — the full process snapshot underneath
  • top / htop — interactive, with a built-in k key to send signals
  • systemctl — the modern way to manage a known service on systemd-managed daemons
  • nginx / apache — the canonical SIGHUP-reload targets
  • strace — pair with pkill -STOP to inspect a frozen process
  • xargs — the alternative when you want pgrep + a tool other than pkill
  • signal / process / PID / /proc — the concepts behind the command

About to pkill something on a production box and not 100% sure what will go down with it?

CleverUptime keeps a live inventory of every process, PID, parent, and cgroup on every server you run — so you can see exactly what a pkill pattern would match before you type it, and the moment a daemon you just pkill -HUP'd fails to come back, the alert is in your inbox with the recent log lines attached.

Want to see your own server's health right now? One command, no signup, no install.

Check your server →