cheat sheet

df, du & duf

Check filesystem free space (df), measure directory sizes (du), and view a colourful disk overview (duf). Covers all key flags, human-readable output, modern alternatives (dust, gdu, ncdu), and common sysadmin recipes.

df, du & duf — Disk Usage

What it is

df and du are POSIX standard disk-reporting utilities present on every Unix and Linux system: df shows free and used space per mounted filesystem, while du measures the disk space consumed by files and directories. duf is a modern, open-source Go-based alternative (v0.9.1, Sept 2025) maintained on GitHub that presents the same information in a colourful, human-friendly table. Reach for df when you need a quick filesystem-level view, du when hunting for large directories, duf when you want an at-a-glance overview of all mounts, and the newer dust / gdu / ncdu alternatives covered below when you need visual tree output or interactive cleanup.


df — Disk Free (filesystem summary)

df reports the amount of disk space used and available on every mounted filesystem.

Common flags

FlagMeaning
-hHuman-readable sizes (K, M, G)
-HHuman-readable, powers of 1000 (not 1024)
-TShow filesystem type
-t TYPEShow only filesystems of TYPE
-x TYPEExclude filesystem type
-iShow inode usage instead of blocks
-lOnly local filesystems
--totalAdd grand total row
bash
df -h                    # all mounts, human-readable

Output:

bash
Filesystem       Size  Used Avail Use% Mounted on
/dev/sda1         50G   18G   30G  38% /
/dev/sda2        200G   82G  108G  44% /home
tmpfs            2.0G  1.2M  2.0G   1% /tmp
tmpfs            2.0G     0  2.0G   0% /dev/shm
bash
df -hT                   # include filesystem type

Output:

bash
Filesystem     Type   Size  Used Avail Use% Mounted on
/dev/sda1      ext4    50G   18G   30G  38% /
/dev/sda2      ext4   200G   82G  108G  44% /home
tmpfs          tmpfs  2.0G  1.2M  2.0G   1% /tmp
tmpfs          tmpfs  2.0G     0  2.0G   0% /dev/shm
code
df -h /home              # specific mount point
df -h /dev/sda1          # specific device
df -i                    # inode usage (when "no space" but df -h looks fine)

Output:

bash
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/sda1      3276800 187432 3089368    6% /
/dev/sda2     13107200 412810 12694390    4% /home
tmpfs           511910    612  511298    1% /tmp
bash
df -h -t ext4            # only ext4 filesystems
df -h -x tmpfs -x devtmpfs  # skip tmpfs/devtmpfs
df -h --total            # with grand total

Output:

bash
Filesystem       Size  Used Avail Use% Mounted on
/dev/sda1         50G   18G   30G  38% /
/dev/sda2        200G   82G  108G  44% /home
total            250G  100G  138G  42%

Interpreting output

text
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   18G   30G  38% /
tmpfs           2.0G  100M  1.9G   5% /dev/shm
  • Use% above ~85% → time to investigate or expand
  • Avail = 0 with non-zero Used → filesystem full
bash
# Alert when any filesystem exceeds 90%
df -h | awk 'NR>1 && $5+0 > 90 {print "WARN:", $6, "is", $5, "full"}'

Output:

vbnet
WARN: /var/log is 93% full
WARN: /boot is 91% full

du — Disk Usage (per directory/file)

du estimates file space usage — the sum of blocks used by each file.

Common flags

FlagMeaning
-hHuman-readable
-sSummary (total for each argument)
-cGrand total at the end
-d N / --max-depth=NDescend at most N levels
-aShow all files, not just directories
-xStay on one filesystem
--exclude=PATTERNSkip matching files/dirs
-lCount hard-linked files each time
--apparent-sizeActual file size (not disk blocks)
-bByte counts
--timeShow last modification time
bash
du -sh *                  # size of each item in cwd

Output:

code
4.2G    Documents
1.8G    Downloads
512M    Music
 48M    Pictures
 12K    notes.txt
 80M    projects
bash
du -sh /var/log           # total size of /var/log
du -sh ~/Downloads/*      # size of each download
du -h --max-depth=1 /     # one level deep from root

Output:

bash
 15M    /bin
4.5G    /home
512M    /lib
 48M    /opt
3.2G    /usr
1.1G    /var
bash
du -h -d 2 /home          # two levels from /home
du -ahc /etc/*.conf       # all .conf files + total

# Sort by size (largest first)
du -sh * | sort -rh | head -20

Output:

code
4.2G    Documents
 80M    projects
 48M    Pictures
1.8G    Downloads
512M    Music
 12K    notes.txt
bash
# Find the biggest directories anywhere under /var
du -h /var | sort -rh | head -20

# Exclude patterns
du -sh --exclude=".git" --exclude="node_modules" .

Output: (none — exits 0 on success)

Find what's eating space

bash
# Step-by-step drill-down
du -h --max-depth=1 /         # find big top-level dirs

Output:

bash
 15M    /bin
4.5G    /home
512M    /lib
 48M    /opt
3.2G    /usr
1.1G    /var
 12G    /
bash
du -h --max-depth=1 /var      # drill into /var
du -h --max-depth=1 /var/log  # drill into logs

# Top 20 largest files anywhere on the filesystem
find / -xdev -type f -printf '%s\t%p\n' 2>/dev/null \
  | sort -rn | numfmt --to=iec-i --suffix=B --field=1 | head -20

# Largest files under current dir
du -ah . | sort -rh | head -20

Output: (none — exits 0 on success)

Watch for growth

bash
# Compare directory size over time
du -sh /var/log > /tmp/before.txt
sleep 3600
du -sh /var/log > /tmp/after.txt
diff /tmp/before.txt /tmp/after.txt

Output: (none — exits 0 on success)


duf — Disk Usage/Free Utility

duf is a modern, colourful replacement for df with better layout and filtering, written in Go by Christian Muehlhaeuser. Reach for it when you want a single command that shows local, network, and FUSE mounts side-by-side with sortable columns and JSON output for scripting. The current release is v0.9.1 (September 2025); the project is in maintenance mode but still ships occasional bug-fix releases.

Installation

bash
sudo apt install duf        # Debian/Ubuntu (20.10+)
brew install duf            # macOS
scoop install duf           # Windows
# or download a release binary from: github.com/muesli/duf/releases

Output: (none — exits 0 on success)

Usage

bash
duf                         # all mounts, colourful table
duf /home /tmp              # specific paths
duf --only local            # only local filesystems
duf --only network          # only network mounts
duf --only fuse             # only FUSE mounts
duf --hide-fs tmpfs,devtmpfs  # hide specific filesystem types
duf --inodes                # show inode usage
duf --output mountpoint,size,used,avail,usage  # custom columns
duf --sort size             # sort by size
duf --sort usage            # sort by usage %
duf --json                  # machine-readable JSON
duf --theme light           # light theme
duf --no-color              # disable colour
duf --width 100             # set terminal width
duf --all                   # include special/pseudo filesystems

Output: (none — exits 0 on success)

Available columns for --output

mountpoint, size, used, avail, usage, type, filesystem, inodes, inodes_used, inodes_avail, inodes_usage


Combining them

bash
# Full disk health check
echo "=== Filesystem Usage ===" && df -hT
echo "=== Top Dirs Under /var ===" && du -h --max-depth=2 /var | sort -rh | head -10
echo "=== Inode Usage ===" && df -i | awk '$5+0 > 50'

# Find and delete old core dumps eating space
find / -xdev -name "core" -type f -size +100M -print0 | xargs -0 ls -lh

Output: (none — exits 0 on success)

-H vs -h: 1000 vs 1024

df -h and du -h use powers of 1024 (KiB, MiB, GiB) but label them with the SI-looking abbreviations K, M, G. -H (capital) switches to powers of 1000, which matches the marketing capacity printed on drive labels. Use -H when reconciling a df figure against a manufacturer's spec sheet, and -h everywhere else.

bash
df -h /                # 50G = 50 × 1024^3 bytes ≈ 53.7 billion bytes
df -H /                # 53G = 53 × 1000^3 bytes ≈ 53.7 billion bytes

Output (df -h /):

bash
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   18G   30G  38% /

Output (df -H /):

bash
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        54G   20G   33G  38% /

Inode exhaustion debugging

A filesystem can refuse new file creation while df -h still shows abundant free space — the symptom is "No space left on device" on touch, mkdir, or cp. The culprit is inode exhaustion: every file consumes one inode, and ext-family filesystems fix the inode count at mkfs time. Run df -i to see inode usage as a parallel "Use%" figure.

bash
df -ih                       # human-readable inode usage

Output:

bash
Filesystem     Inodes IUsed IFree IUse% Mounted on
/dev/sda1        3.1M  187K  2.9M    6% /
/dev/sda2       12.5M  413K   12M    4% /home
/dev/sdb1        488K  488K     0  100% /var/cache  ← inode-full
bash
# Find directories with millions of small files
sudo find /var -xdev -type d -print0 \
  | xargs -0 -I{} sh -c 'echo "$(find "{}" -maxdepth 1 | wc -l) {}"' \
  | sort -rn | head -10

# Common offenders: session caches, mail spools, Maildir, npm caches
sudo find /var/cache -xdev -type d -links +2 -printf '%p\n' \
  | xargs -I{} sh -c 'echo "$(ls -A1 "{}" 2>/dev/null | wc -l) {}"' \
  | sort -rn | head

Output (sample):

swift
3142187 /var/cache/php/sessions
   8412 /home/alice/.npm/_cacache/content-v2
   2103 /var/spool/postfix/maildrop

df vs du discrepancy

df reads filesystem allocation tables; du walks the directory tree and sums file sizes. They diverge when the filesystem allocates space that the directory tree no longer references — most commonly because a running process is holding a file descriptor to a file that's been unlinked. The blocks stay allocated until the process closes the fd or exits.

bash
# Compare df and du for /var
df -h /var
du -sh /var

# If df reports much more used than du, hunt for deleted-but-open files
sudo lsof -nP +L1 | head -20                # files with link-count 0
sudo lsof | awk '/deleted/ {print $1, $2, $7, $NF}' | sort -rk3 | head

Output (lsof | grep deleted):

swift
nginx     1234   /var/log/nginx/error.log.1 (deleted)  4.2G
postgres  5678   /var/lib/postgresql/wal/000001 (deleted)  1.8G
bash
# Free the space without restart: truncate via /proc
sudo : > "/proc/1234/fd/3"        # if fd 3 is the deleted log

# Or restart the holding process
sudo systemctl restart nginx

Output: (none — exits 0 on success)

See also: lsof | grep deleted in ps, netstat & lsof.

--apparent-size vs allocated blocks

By default du reports the disk blocks consumed, which is always a multiple of the filesystem block size (usually 4 KiB). --apparent-size reports the file's logical byte length, which is smaller for sparse files (e.g. VM images, qcow2, large database files with holes) and larger if the file has a tiny payload that still occupies a full block.

bash
du -sh image.qcow2                # 4.2G — what's on disk
du -sh --apparent-size image.qcow2 # 40G — virtual size (mostly holes)

# Many tiny files: apparent-size is much smaller
du -sb -d 1 /etc                  # bytes allocated
du -sb --apparent-size -d 1 /etc   # bytes in payload

Output: (none — exits 0 on success)

Sorting du output safely

sort -h (human-numeric) understands K, M, G suffixes, but only GNU sort supports it — BSD/macOS users need sort -n after stripping suffixes, or gsort -h from coreutils. The pattern below is the safest cross-platform way to find the biggest entries in a directory.

bash
# GNU systems
du -sh ./* | sort -rh | head -10

# macOS / BSD (use gnu-coreutils or numfmt)
du -sk ./* | sort -rn | head -10 | numfmt --field=1 --from-unit=1024 --to=iec

# Top 20 biggest files anywhere under cwd
du -ah . 2>/dev/null | sort -rh | head -20

# Biggest dirs at exactly depth 2
du -h --max-depth=2 / 2>/dev/null | sort -rh | head -20

Output:

bash
4.2G    ./Videos
1.8G    ./Downloads/iso
512M    ./Music
 80M    ./projects
 48M    ./Pictures

ncdu — interactive du

ncdu is an interactive, curses-based du viewer that lets you navigate a directory tree, sort by size, and delete entries inline. Reach for it when you need to find and remove large content rather than just measuring it — the keyboard-driven UI is much faster than repeated du --max-depth runs.

bash
sudo apt install ncdu        # Debian/Ubuntu
brew install ncdu            # macOS

ncdu /                       # scan and browse from root
ncdu -x /                    # stay on one filesystem
ncdu -o scan.json /var       # save scan to a JSON file
ncdu -f scan.json            # browse a previous scan offline
ncdu --exclude '.git' .      # skip patterns

Output (interactive — sample first screen):

css
--- / ----------------------------------
  4.5 GiB [##########] /home
  3.2 GiB [#######   ] /usr
  1.1 GiB [##        ] /var
  512 MiB [#         ] /lib
   48 MiB [          ] /opt
   15 MiB [          ] /bin

Keys: d delete, n sort by name, s sort by size, g toggle percent/graph, ? help, q quit.

dust — visual du in Rust

dust is a Rust-based du replacement (v1.2.4, January 2026) that renders a colour bar-chart tree of the largest subdirectories up to the terminal height — no | sort -rh | head pipeline required. Reach for it when you want a one-shot "what's eating my disk?" view without launching an interactive TUI; it respects .gitignore, sorts largest-first by default, and is noticeably faster than du on large trees because it parallelises the walk.

bash
brew install dust            # macOS / Linuxbrew
cargo install du-dust        # any platform with Rust
sudo apt install du-dust     # Debian 12+ / Ubuntu 23.10+

dust                         # tree of current dir
dust /var                    # tree under /var
dust -d 3                    # cap depth at 3
dust -n 30                   # show top 30 entries
dust -r                      # reverse: smallest first
dust -s                      # show apparent sizes
dust -x                      # stay on one filesystem
dust -i                      # ignore .gitignore'd files (default respects it)
dust -t 4                    # use 4 threads
dust -P                      # print full paths

Output (top-of-tree sample):

arduino
  4.5G  ┌── home         │█████████             │ 36%
  3.2G  ├── usr          │██████                │ 26%
  1.1G  ├── var          │██                    │  9%
  512M  └── lib          │█                     │  4%
 12.4G  /                │██████████████████████│100%

gdu — Go du with TUI and CLI modes

gdu is a Go-rewrite of du that runs in either an interactive ncurses-style TUI (similar to ncdu) or a one-shot non-interactive mode. It parallelises the directory walk and is typically the fastest of the alternatives on NVMe storage. Reach for it when you want ncdu's navigation but with throughput closer to native du, or when you need a non-interactive output for piping.

bash
brew install gdu             # macOS / Linuxbrew
sudo apt install gdu         # Debian 12+ / Ubuntu 23.10+

gdu                          # interactive TUI in cwd
gdu /var                     # interactive TUI under /var
gdu -n /var                  # non-interactive, prints summary
gdu -np /var                 # no-progress, scripting-friendly
gdu -x /                     # stay on one filesystem
gdu -o report.json /var      # export scan as JSON
gdu -f report.json           # browse a saved scan
gdu --no-cross               # don't cross filesystem boundaries
gdu --ignore-dirs /proc,/sys /

Output: (none — exits 0 on success)

Inside the TUI: d delete, r rescan, n/s/C sort by name/size/items, t show modification time, q quit.

Comparison table

ToolTypeLayerBest forNotes
dffilesystem summaryblockQuick "am I out of space?"POSIX-standard, on every system
dufile-tree walkpathFinding biggest dirs/filesSlow on huge trees
duffilesystem summaryblockColourful dashboard viewGo binary, JSON output, v0.9.1 (Sept 2025)
dustfile-tree walkpathOne-shot visual tree of largest dirsRust, parallel walk, gitignore-aware
gdufile-tree walker (TUI + CLI)pathFast interactive or scriptable analysisGo, fastest on NVMe, JSON export
ncdufile-tree walker (TUI)pathInteractive cleanupCurses UI, delete-in-place
lsof +L1open-file inspectorprocessDeleted-but-open file culpritsRequired when df > du

Filesystem cleanup recipes

bash
# 1. Find the top 20 biggest files anywhere on the root filesystem
sudo find / -xdev -type f -printf '%s\t%p\n' 2>/dev/null \
  | sort -rn | head -20 \
  | numfmt --to=iec-i --suffix=B --field=1

# 2. Find files older than 90 days under /var/log (candidates for deletion)
sudo find /var/log -xdev -type f -mtime +90 -printf '%TY-%Tm-%Td %s %p\n' \
  | sort | head -50

# 3. Find directories with the most files (inode hogs)
sudo find / -xdev -type d -print0 2>/dev/null \
  | xargs -0 -I{} sh -c 'echo "$(ls -A1 "{}" 2>/dev/null | wc -l) {}"' \
  | sort -rn | head -20

# 4. Show package-manager cache sizes
du -sh /var/cache/apt /var/cache/dnf /var/cache/yum ~/.cache 2>/dev/null

# 5. Show systemd journal disk usage
journalctl --disk-usage
sudo journalctl --vacuum-size=500M     # cap at 500 MB

# 6. Find Docker disk usage
docker system df
docker system prune -af                # remove all unused images/containers

# 7. Trim a partition without rebooting (ext4 with discard)
sudo fstrim -av

# 8. Empty trash and old backups
rm -rf ~/.local/share/Trash/files/*
find /backups -name '*.tar.gz' -mtime +30 -delete

# 9. Big files modified in the last day (recent culprits)
sudo find / -xdev -type f -size +100M -mtime -1 2>/dev/null

# 10. Cleanup pip / npm / cargo caches
pip cache purge
npm cache clean --force
cargo cache --autoclean   # via the cargo-cache crate

Output: (varies — exits 0 on success)

Monitoring disk over time

For a quick continuous view, pair watch with df -h; for historical trending, log to a file and graph later. duf --json is useful inside Prometheus exporters or status scripts.

bash
# Live refresh every 2 s
watch -n 2 'df -h | grep -v tmpfs'

# Log df every 5 minutes
while true; do
  echo "=== $(date -Iseconds) ===" >> /var/log/diskwatch.log
  df -h >> /var/log/diskwatch.log
  sleep 300
done &

# Alert when /var crosses 80%
df -h /var | awk 'NR==2 && $5+0 > 80 {print "WARN /var at "$5}'

Output: (none — exits 0 on success)

Edge cases and gotchas

SymptomCauseFix
df shows full, du is much smallerDeleted files held by running processeslsof | grep deleted, restart process
"No space left" but df -h shows freeInode exhaustiondf -i, delete millions of small files
du slower than expectedCrossing into network/FUSE mountsAdd -x to stay on one filesystem
du and ls -l disagreeSparse files, hardlinks, or block alignmentdu --apparent-size for logical size
df shows 100% but writes workReserved blocks (5% for root by default)tune2fs -m 1 /dev/sdaN to lower reserve
du -sh /proc gives weird huge numbers/proc is a virtual filesystemAlways add -x or skip /proc and /sys
Permission denied warnings flood stderrWalking dirs the user can't readAdd 2>/dev/null or run with sudo

When df -h shows a filesystem full but du -sh doesn't account for all the space, look for deleted but open files: lsof | grep deleted | awk '{print $7, $NF}' | sort -rh | head -10. A running process may hold file descriptors to deleted log files, keeping the blocks allocated.

ext-family filesystems reserve 5% of blocks for root by default. On data-only volumes this is wasted space; reclaim it with sudo tune2fs -m 1 /dev/sdaN (1% reservation).

Always add -x to du and find when starting from / — otherwise the walk descends into /proc, /sys, network mounts, and bind mounts, inflating sizes and exposing permission-denied errors.

Sources