htop — Interactive Process Viewer#
What it is#
htop is an interactive, color, full-screen process and resource monitor for the terminal, written in C by Hisham Muhammad in 2004 (and maintained today by the htop-dev collective). It reads /proc to show every running process, sortable and filterable in real time, plus CPU, memory, and swap meters across the top. Reach for htop whenever a one-shot ps aux or top snapshot isn’t enough — it’s the standard tool for “what’s hogging the CPU?” or “kill this stuck worker”; top is the always-present POSIX fallback, and btop (newer, written in C++) is the visually richer alternative.
Install#
htop is packaged on every mainstream Linux distribution and macOS via Homebrew, and ships as a single static binary if you need to drop it on a stripped-down container.
# Debian/Ubuntu
sudo apt install htop
# Fedora
sudo dnf install htop
# Arch
sudo pacman -S htop
# macOS
brew install htop
# Alternatives
brew install btop # nicer UI, more meters
# top ships with every Unix already
Output: (none — exits 0 on success)
Syntax#
htop is launched without arguments for the default view, or with a small set of flags to pre-filter what it shows. Once running, all interaction is keyboard-driven (mouse is supported in most terminals).
htop [OPTIONS]
htop -p PID[,PID...]
htop -u USER
htop -s COLUMN
Output: (none — exits 0 on success)
Essential CLI options#
| Option | Meaning |
|---|---|
-p PID[,PID...] | Watch only the listed PIDs |
-u USER | Show only USER’s processes |
-s COLUMN | Sort by COLUMN at startup (e.g. PERCENT_CPU, PERCENT_MEM) |
-d DELAY | Refresh delay in tenths of a second (default 15 = 1.5s) |
-t | Start in tree view |
-H | Show user threads (kernel threads off) |
-C | Monochrome (no colors) — NO_COLOR=1 env var works too (3.5+) |
--no-mouse | Disable mouse support |
--no-meters | Hide the header meters (3.5+) |
--no-function-bar | Hide the F1–F10 bar at the bottom (3.5+) |
--readonly | Disable kill / nice / setpriority (safe for shared boxes) |
--help | Show all options |
--version | Print version |
Default screen layout#
The interface is divided into a header (CPU/memory/swap meters plus load average and uptime), the process list (scrollable, sortable table), and a function-key bar at the bottom showing F1–F10 actions.
1 [||||||||||||||| 38.2%] Tasks: 142, 287 thr; 3 running
2 [||||||||||||||||||||||||||| 72.4%] Load average: 0.84 0.61 0.40
3 [|||||||||| 21.7%] Uptime: 2 days, 04:13:55
4 [|||||||||||| 27.0%]
Mem[||||||||||||||| 3.42G/15.6G]
Swp[ 0K/2.00G]
PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command
4521 alice 20 0 712M 402M 20M R 88.4 2.6 1:42.11 python3 train.py
1234 www-data 20 0 124M 15M 12M S 0.7 0.1 0:02.31 nginx: worker
8800 root 20 0 23M 6.4M 5.6M S 0.0 0.0 0:00.04 sshd
8821 alice 20 0 18M 4.2M 3.4M S 0.0 0.0 0:00.02 -bash
...
F1Help F2Setup F3Search F4Filter F5Tree F6SortBy F7Nice- F8Nice+ F9Kill F10Quit
Function keys#
F1–F10 are the discoverable, always-visible actions. They also have single-letter equivalents that don’t require an Fn key (useful in macOS terminals where function keys go to the system).
| Key | Alt | Action |
|---|---|---|
F1 | h | Help screen |
F2 | S | Setup — meters, columns, colors |
F3 | / | Search (incremental, highlights matches) |
F4 | \\ | Filter (only matching processes shown) |
F5 | t | Toggle tree view |
F6 | < > | Choose sort column |
F7 | [ | Decrease nice value (raise priority) |
F8 | ] | Increase nice value (lower priority) |
F9 | k | Kill — send signal to selected process |
F10 | q | Quit |
htop # then press F4, type "nginx", Enter — only nginx procs remain
Output:
PID USER CPU% MEM% Command
1234 www-data 0.5 0.1 nginx: master process /usr/sbin/nginx
1235 www-data 0.3 0.1 nginx: worker process
1236 www-data 0.3 0.1 nginx: worker process
Sorting keys#
< and > move through the sort-column list; the on-screen heading shows the active column highlighted. The three most-used one-key shortcuts pre-select the typical sorts:
| Key | Sort by |
|---|---|
M | Memory (descending) |
P | CPU% (descending) |
T | Cumulative TIME+ |
N | PID |
I | Invert current sort order |
htop -s PERCENT_MEM # pre-sort by RAM at launch
htop -s TIME # pre-sort by cumulative CPU time
Output (M sort):
PID USER MEM% RES Command
4521 alice 2.6 402M python3 train.py
2100 postgres 1.2 192M postgres
1234 www-data 0.1 15M nginx: master
Tree view (F5 / t)#
Tree view replaces the flat process list with parent → child indentation, so you can see at a glance which daemon spawned a runaway worker. Toggling it preserves the current sort and filter; the indented children still sort by the active column within each parent.
htop -t # start in tree view
# or press F5 / t at any time to toggle
Output:
PID USER CPU% MEM% Command
1 root 0.0 0.0 /sbin/init
423 root 0.0 0.1 ├─ systemd-journald
611 root 0.0 0.0 ├─ cron
800 root 0.0 0.0 ├─ sshd
8800 root 0.0 0.0 │ └─ sshd: alicedev [priv]
8820 alice 0.0 0.0 │ └─ sshd: alicedev@pts/0
8821 alice 0.0 0.0 │ └─ -bash
9210 alice 0.0 0.0 │ └─ htop
1234 www-data 0.5 0.1 └─ nginx: master process
1235 www-data 0.3 0.1 ├─ nginx: worker
1236 www-data 0.3 0.1 └─ nginx: worker
Threads (H)#
By default htop shows one row per process. Pressing H toggles user-thread visibility, which is essential for diagnosing multi-threaded apps (a JVM, a Python process with thread pools) — without it, a 16-thread process eating 800% CPU shows up as a single 800% row with no way to see which thread is hot. K toggles kernel threads on/off similarly.
htop -H # threads visible at launch
Output (Java app, H enabled):
PID USER CPU% Command
5821 alice 104.0 java -jar app.jar
5822 alice 32.1 GC Thread#0
5823 alice 28.7 GC Thread#1
5824 alice 18.2 main
5825 alice 12.0 http-worker-1
Search vs. filter (F3 vs. F4)#
These look similar but behave differently. Search (F3 / /) jumps the cursor to each matching process but keeps every row visible; press the key repeatedly to walk through hits. Filter (F4 / \\) hides every non-matching row until you press F4 again with an empty query.
# Inside htop:
# /nginx → cursor jumps from match to match; all rows still visible
# \nginx → only matching rows shown; counter at top updates
# \ → with empty pattern, restores full list
Output (after \\nginx):
PID USER CPU% Command
1234 www-data 0.5 nginx: master process
1235 www-data 0.3 nginx: worker process
Sending signals (F9 / k)#
Pressing F9 opens a signal-picker side panel; the default is SIGTERM (15) — polite. Use SIGKILL (9) only as a last resort, after SIGTERM and SIGINT (2) have been ignored. SIGHUP (1) is the canonical “reload config” signal for daemons.
| Signal | Number | Use |
|---|---|---|
SIGTERM | 15 | Polite “please exit” (default) |
SIGINT | 2 | Like Ctrl-C |
SIGHUP | 1 | Reload config (for daemons) |
SIGKILL | 9 | Force kill — process can’t catch or ignore |
SIGSTOP / SIGCONT | 19 / 18 | Pause / resume |
SIGUSR1 / SIGUSR2 | 10 / 12 | App-defined |
Send signal:
✱ 15 SIGTERM
9 SIGKILL
2 SIGINT
1 SIGHUP
...
You can also select multiple processes with Space (toggles a tag), then press F9 to signal all tagged processes at once.
Watching a single PID (-p)#
htop -p <PID> (one or comma-separated PIDs) pins the view to just those processes, with the meters still showing system-wide stats. Combined with pgrep, this is the quickest way to live-monitor one service.
htop -p $(pgrep -d, nginx) # all nginx PIDs as a single arg
htop -p $(pgrep -d, -f 'python.*train.py')
Output:
PID USER CPU% MEM% Command
1234 www-data 0.5 0.1 nginx: master process
1235 www-data 0.3 0.1 nginx: worker
1236 www-data 0.3 0.1 nginx: worker
Setup (F2)#
F2 opens a configuration screen where you can change meters in the header, add or remove columns from the process list, and pick a color scheme. Changes are written to ~/.config/htop/htoprc and persist across launches.
Active Available
Left column Meters
> CPU - CPU
> Memory - Memory
- Swap
Right column - Load average
> Tasks - Uptime
> Load average - Battery
> Uptime - Hostname
- All CPUs
- Disk IO
- Network IO
# Common customisations
# - Show all CPU cores as one bar to save space:
# F2 → Meters → CPUs (1/1) [Bar] or [Text]
# - Add Disk IO and Network IO meters
# - Add IO_RATE column to the process list
Output: (none — exits 0 on success)
Configuration#
htop persists everything you change in F2 Setup to ~/.config/htop/htoprc — meters, columns, colour scheme, refresh delay, hide-kernel-threads, and so on. The file is plain key=value text and is safe to edit by hand, copy between machines, or version-control alongside your dotfiles. Since 3.4 htop and pcp-htop keep separate config files so the two don’t clobber each other.
# Inspect the live config
cat ~/.config/htop/htoprc
# Back up before experimenting
cp ~/.config/htop/htoprc ~/.config/htop/htoprc.bak
# Reset to defaults (htop rewrites the file on next quit)
rm ~/.config/htop/htoprc
htop # change nothing, then F10 — fresh file is written
Output (cat, abridged):
fields=0 48 17 18 38 39 40 2 46 47 49 1
sort_key=46
sort_direction=-1
tree_sort_key=0
hide_kernel_threads=1
hide_userland_threads=0
shadow_other_users=0
show_thread_names=0
show_program_path=1
color_scheme=0
delay=15
left_meters=AllCPUs Memory Swap
left_meter_modes=1 1 1
right_meters=Tasks LoadAverage Uptime
right_meter_modes=2 2 2
| Key | Meaning |
|---|---|
fields=… | Process-list columns, by numeric column ID |
sort_key / sort_direction | Default sort column and direction (-1 desc) |
delay | Refresh delay in tenths of a second |
hide_kernel_threads / hide_userland_threads | What to hide by default |
show_program_path | 1 = full path, 0 = command basename only |
color_scheme | 0=default, 1=monochrome, 5=broken-grey, 6=Nord (3.5+) |
left_meters / right_meters | Header meters by name (CPU, Memory, GPU, DiskIO, …) |
htop writes the file when you quit cleanly; if you edit it while htop is running, your changes will be overwritten on exit. Make sure no other instance is active before hand-editing.
What’s new in htop 3.4 and 3.5#
The htop-dev team have been steadily landing modern monitoring features. 3.4 (March 2025) added a GPU meter and per-process GPU time column — the first time htop could show NVIDIA/AMD GPU utilisation alongside CPU. 3.5 (released later in 2025) added a backtrace screen built on libunwind-ptrace so you can inspect the call stack of any process without dropping to gdb, plus a clean Nord colour theme, a proper inline line editor for search/filter/screen-rename, and --no-meters / --no-function-bar for embedding htop in tmux panes without visual clutter.
| Version | Headline additions |
|---|---|
| 3.5.x | Backtrace screen (libunwind-ptrace); Nord theme; line editor for search/filter; --no-meters, --no-function-bar; NO_COLOR env support; CPU SMT label option; redesigned DiskIO meter (rate + time sub-meters); macOS GPU meter; OpenRC support |
| 3.4.x | GPU meter (Linux + PCP); per-process GPU time column; single-column header layout; per-platform config file (~/.config/htop/htoprc vs. pcp-htop); dynamic CPU-column width based on max PID; better ARM Darwin support |
| 3.3.x | Docker / Podman container ID shortening (12 chars); FreeBSD truss support; Darwin NetworkIOMeter; 3-column header layouts; container-name filtering from cgroup names |
# Add the GPU meter (3.4+) — F2 → Meters → GPU → add to a column
# Add the GPU time column — F2 → Columns → GPU_TIME
# Open the backtrace screen (3.5+) — select a process, then
# F2 → Screens → add "Backtrace" → assign to F6 or a Tab key
# Embed htop in a tmux pane with no chrome
htop --no-meters --no-function-bar
Output: (none — exits 0 on success)
Reading the header#
The header has more nuance than it looks. Load average is a running average of runnable + uninterruptible processes over 1, 5, and 15 minutes — not a CPU percentage. Mem colors mean: green = used, blue = buffers, yellow = cache. The cache portion is “free if anything else needs it” — Linux deliberately uses spare RAM as disk cache, so a host showing 90% used is usually healthy.
| Display | What to look for |
|---|---|
Load average: 1.20 0.80 0.40 | First number > number of CPU cores → over-saturated |
| `Mem[ | |
Swp[ 0K/2.00G] | Any swap use is a yellow flag; sustained use is a red flag |
Tasks: 142, 287 thr; 3 running | ”3 running” should generally be ≤ core count |
# Sanity-check load average against core count
nproc
uptime
Output:
4
10:14:33 up 2 days, 4:13, 1 user, load average: 0.84, 0.61, 0.40
htop vs. top vs. btop vs. bottom#
top is POSIX and present on every Unix — when you SSH into a box with nothing installed, it’s the fallback. htop is the everyday upgrade: colour, scrollable, mouse-aware, function keys for kill/nice/sort, and (since 3.4/3.5) GPU meters and a backtrace screen. btop (the C++ successor to bpytop, which was itself the Python successor to bashtop) is the maximalist option — Braille graphs for CPU/network/disk, mouse-driven menus, animated time-series. bottom (binary name btm) is the keyboard-first Rust alternative with a unique TOML layout system so you can carve the screen into any arrangement of panes you like. bpytop itself is no longer actively developed — it’s been fully superseded by btop (and the Rust rewrite is now landing in distros as btop 4.x).
| Feature | top | htop | btop | bottom (btm) |
|---|---|---|---|---|
| In every base install | yes | no | no | no |
| Language / runtime | C | C | C++ (Rust 4.x) | Rust |
| Colour UI | optional | yes | yes | yes |
| Mouse support | no | yes | yes | yes |
| Scroll horizontally | no | yes | yes | yes |
| Tree view | partial (V) | yes (F5) | yes | yes |
| Per-thread view | yes (H) | yes (H) | yes | no |
| CPU/Net/Disk graphs | no | basic meters | rich Braille | rich Braille |
| GPU meter | no | yes (3.4+) | yes | yes |
| Backtrace screen | no | yes (3.5+) | no | no |
| Custom layout | no | header only | preset views | full TOML config |
| Colour themes | no | a few (Nord 3.5+) | many | many |
| Footprint | tiny | small | medium | small |
top -o +%CPU # POSIX top, sorted by CPU
top -H # show threads (Linux top)
btop # if installed; q to quit
btm # bottom; press ? for help, q to quit
btm --basic # bottom in compact, no-graph mode
Output (top -o +%CPU, abridged):
top - 10:14:33 up 2 days, 4:13, 1 user, load average: 0.84, 0.61, 0.40
Tasks: 142 total, 3 running, 139 sleeping
%Cpu(s): 38.2 us, 5.1 sy, 0.0 ni, 56.0 id
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4521 alice 20 0 712340 402100 20012 R 88.4 2.6 1:42.11 python3
1235 www-data 20 0 126484 15280 12044 S 0.7 0.1 0:02.31 nginx
Rule of thumb: reach for top on a stripped-down jumpbox, htop for everyday triage and the backtrace screen, btop when you want pretty graphs over time on a desktop or home-lab box, and bottom when you need a custom multi-pane layout you can check into dotfiles.
Common pitfalls#
- “100% CPU” with one core saturated — the per-process
CPU%is summed across cores, so a single thread maxing one of four cores reads as100%, not25%. Toggle to system-percent in Setup if you want the latter. - Memory looks “full” but the system is fine — yellow bar segments are file cache that Linux will release on demand. Real pressure shows up as growing
Swpusage orD-state processes blocked on I/O. - Load average ≠ CPU percentage — a load of
8on a 16-core box is half-utilised; a load of8on a 4-core box is queueing two-deep. Always compare tonproc. - Threads invisible — by default
htophides user threads; pressH(or launch with-H) when investigating Java/Python/Go programs. - Sort column won’t stay set — in tree view sorting groups within siblings only; press
F5again to leave tree mode if you need a global sort. - Function keys hijacked by terminal — iTerm2 and macOS Terminal sometimes swallow
F2/F9. Use the letter equivalents (S,k) instead, or remap in Terminal preferences. htopshows wrong colors over SSH —TERMis set toxterminstead ofxterm-256color. Runexport TERM=xterm-256color(ortmux-256colorinside tmux).
Real-world recipes#
Find the top three CPU eaters and kill the worst#
A two-shortcut workflow: P sorts by CPU, Space tags processes, F9 kills them all at once.
htop
→ press P # sort by CPU
→ Space x3 on top 3 # tag them
→ F9 → 15 # SIGTERM tagged processes
Output: (the tagged rows disappear within a refresh tick)
PID USER CPU% Command
1234 www-data 0.5 nginx: master
8821 alice 0.0 -bash
9210 alice 0.0 htop
Watch a single misbehaving service#
When a service is the suspect, narrow the picker to just its PIDs and turn threads on so you can see which worker is hot.
htop -H -p $(pgrep -d, -f 'gunicorn|uwsgi')
Output:
PID USER CPU% MEM% Command
5021 www-data 62.1 3.4 gunicorn: worker [app:wsgi]
5022 www-data 41.9 3.2 gunicorn: worker [app:wsgi]
5020 www-data 0.1 3.1 gunicorn: master [app:wsgi]
“What changed?” snapshot for a bug report#
Press F2 → enable “All CPUs (1/1) [Text]” to compact the header, then capture the screen with tmux capture-pane or your terminal’s native screenshot. Pair with journalctl -u myapp -n 50 for the matching log slice.
tmux capture-pane -p -S - > /tmp/htop-snapshot.txt
journalctl -u myapp -n 50 --no-pager > /tmp/myapp-50.log
Output: (none — exits 0 on success)
Stuck process: which syscall is it blocked on?#
htop shows the S state column — D means “uninterruptible sleep” (usually disk I/O). When you spot one, drop to strace -p PID for the syscall it’s stuck in.
htop # find a D-state PID, say 7821
sudo strace -p 7821 -e trace=desc
Output:
strace: Process 7821 attached
read(8, ...
Quick comparison: are these workers balanced?#
Run htop filtered to the worker pool. If one row is at 80% and the others near 0%, the load balancer or work queue is misrouting work.
htop -p $(pgrep -d, -f 'celery worker') -s PERCENT_CPU
Output:
PID USER CPU% Command
8801 alice 78.4 celery worker -A app -Q default
8802 alice 2.1 celery worker -A app -Q default
8803 alice 1.0 celery worker -A app -Q default
8804 alice 0.7 celery worker -A app -Q default
[!TIP] On servers you administer alongside others, run
htop --readonly— it disables kill / nice / setpriority so a misclick can’t take down a process. Combine withalias htop='htop --readonly'for shared bastions.
[!TIP] If you live in tmux, bind a prefix key to “split + run htop”:
bind-key h split-window -h 'htop'. Pressprefix hand you have a live process pane next to whatever you’re working on.