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
| Flag | Meaning |
|---|---|
-h | Human-readable sizes (K, M, G) |
-H | Human-readable, powers of 1000 (not 1024) |
-T | Show filesystem type |
-t TYPE | Show only filesystems of TYPE |
-x TYPE | Exclude filesystem type |
-i | Show inode usage instead of blocks |
-l | Only local filesystems |
--total | Add grand total row |
df -h # all mounts, human-readable
Output:
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
df -hT # include filesystem type
Output:
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
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:
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
df -h -t ext4 # only ext4 filesystems
df -h -x tmpfs -x devtmpfs # skip tmpfs/devtmpfs
df -h --total # with grand total
Output:
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
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
# Alert when any filesystem exceeds 90%
df -h | awk 'NR>1 && $5+0 > 90 {print "WARN:", $6, "is", $5, "full"}'
Output:
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
| Flag | Meaning |
|---|---|
-h | Human-readable |
-s | Summary (total for each argument) |
-c | Grand total at the end |
-d N / --max-depth=N | Descend at most N levels |
-a | Show all files, not just directories |
-x | Stay on one filesystem |
--exclude=PATTERN | Skip matching files/dirs |
-l | Count hard-linked files each time |
--apparent-size | Actual file size (not disk blocks) |
-b | Byte counts |
--time | Show last modification time |
du -sh * # size of each item in cwd
Output:
4.2G Documents
1.8G Downloads
512M Music
48M Pictures
12K notes.txt
80M projects
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:
15M /bin
4.5G /home
512M /lib
48M /opt
3.2G /usr
1.1G /var
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:
4.2G Documents
80M projects
48M Pictures
1.8G Downloads
512M Music
12K notes.txt
# 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
# Step-by-step drill-down
du -h --max-depth=1 / # find big top-level dirs
Output:
15M /bin
4.5G /home
512M /lib
48M /opt
3.2G /usr
1.1G /var
12G /
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
# 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
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
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
# 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.
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 /):
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 18G 30G 38% /
Output (df -H /):
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.
df -ih # human-readable inode usage
Output:
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
# 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):
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.
# 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):
nginx 1234 /var/log/nginx/error.log.1 (deleted) 4.2G
postgres 5678 /var/lib/postgresql/wal/000001 (deleted) 1.8G
# 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.
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.
# 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:
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.
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):
--- / ----------------------------------
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.
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):
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.
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
| Tool | Type | Layer | Best for | Notes |
|---|---|---|---|---|
df | filesystem summary | block | Quick "am I out of space?" | POSIX-standard, on every system |
du | file-tree walk | path | Finding biggest dirs/files | Slow on huge trees |
duf | filesystem summary | block | Colourful dashboard view | Go binary, JSON output, v0.9.1 (Sept 2025) |
dust | file-tree walk | path | One-shot visual tree of largest dirs | Rust, parallel walk, gitignore-aware |
gdu | file-tree walker (TUI + CLI) | path | Fast interactive or scriptable analysis | Go, fastest on NVMe, JSON export |
ncdu | file-tree walker (TUI) | path | Interactive cleanup | Curses UI, delete-in-place |
lsof +L1 | open-file inspector | process | Deleted-but-open file culprits | Required when df > du |
Filesystem cleanup recipes
# 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.
# 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
| Symptom | Cause | Fix |
|---|---|---|
df shows full, du is much smaller | Deleted files held by running processes | lsof | grep deleted, restart process |
"No space left" but df -h shows free | Inode exhaustion | df -i, delete millions of small files |
du slower than expected | Crossing into network/FUSE mounts | Add -x to stay on one filesystem |
du and ls -l disagree | Sparse files, hardlinks, or block alignment | du --apparent-size for logical size |
df shows 100% but writes work | Reserved 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 filesystem | Always add -x or skip /proc and /sys |
| Permission denied warnings flood stderr | Walking dirs the user can't read | Add 2>/dev/null or run with sudo |
When
df -hshows a filesystem full butdu -shdoesn'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
-xtoduandfindwhen starting from/— otherwise the walk descends into/proc,/sys, network mounts, and bind mounts, inflating sizes and exposing permission-denied errors.
Sources
- muesli/duf — Disk Usage/Free Utility (GitHub)
- muesli/duf — Releases (v0.9.1, Sept 2025)
- bootandy/dust — A more intuitive version of du in rust (GitHub)
- Dust: A Modern du Alternative for Visualizing Disk Usage (KX guide)
- Best Console Utilities to Analyze Disk Space Distribution in 2026
- Modern Alternatives to Some of the Classic Linux Commands (It's FOSS)
- 8 Best 'du' [Disk Usage] Command Alternatives for Linux (UbuntuMint)
- duf — Disk Usage/Free Utility for Linux, BSD, macOS & Windows (nixCraft)