Linux Disk Usage: df, du and ncdu Guide

Tested on: Ubuntu 26.04 LTS · Debian 12 · Fedora 44 · Arch Linux — Last updated: June 2026

Your disk is full. The error says "No space left on device." Or maybe disk usage is quietly creeping toward 100% and you want to find the culprit before it causes an outage. This guide gives you the complete workflow: df to identify which filesystem is full, du to hunt down the offending directory, and ncdu for fast interactive exploration — then the most common culprits on real servers and exactly how to reclaim the space.

Contents
  1. Prerequisites
  2. Step 1: df — Identify the Full Filesystem
    1. Don't Forget Inodes
  3. Step 2: du — Drill Down to the Culprit
  4. Step 3: ncdu — Interactive Disk Explorer
  5. Common Culprits and How to Clean Them
    1. Systemd Journal Logs
    2. Package Cache
    3. Docker: Images, Volumes, and Build Cache
    4. Old Log Files Outside Journald
    5. Snap Package Old Revisions
    6. npm Cache and node_modules
    7. Temporary Files and Trash
  6. Proactive Monitoring: Get Alerted Before Disks Fill
    1. Further Reading

Prerequisites

df and du are part of GNU coreutils and are present on every Linux system by default. ncdu requires a separate install (covered below). You'll need sudo access to scan system directories and clean up root-owned files. On systems with Docker, the docker CLI must be installed and the daemon running.

Step 1: df — Identify the Full Filesystem

Start here every time. df shows you the high-level picture: which mounted filesystems exist, how much space each has, and what percentage is consumed. Without this first step, you risk spending time hunting in the wrong place.

# Human-readable sizes — the starting point
df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   47G  1.3G  97% /
/dev/sda2       200G   45G  155G  23% /home
tmpfs           3.9G  1.2M  3.9G   1% /run
/dev/sdb1       500G  480G   20G  96% /var/lib/docker

The Use% column is the one to watch. Anything above 90% deserves attention now. The Mounted on column tells you exactly which filesystem to investigate — in the example above, both / and the Docker volume need work.

Filter out virtual and pseudo-filesystems to reduce noise when the output is cluttered:

# Show only real disk-backed filesystems
df -hT --type=ext4 --type=xfs --type=btrfs --type=zfs

# Show usage for a specific directory's filesystem
df -h /var/log

# Show all filesystems including type labels
df -hT

Don't Forget Inodes

Inode exhaustion is a silent killer: df -h reports free space, but writes still fail because the filesystem has run out of inodes — the metadata entries used to track individual files. This happens with mail queues, PHP session directories, and cache systems that create millions of tiny files.

# Check inode usage across all filesystems
df -i

# Output example:
# Filesystem      Inodes  IUsed   IFree IUse% Mounted on
# /dev/sda1      3276800 3276799       1  100% /
# /dev/sda2      6553600  185432 6368168    3% /home

If IUse% hits 100%, find the directory with millions of files:

# Count files per top-level directory — this may take a while
for dir in /*; do
  echo -n "$dir: "
  find "$dir" -xdev 2>/dev/null | wc -l
done | sort -t: -k2 -rn | head -10

Step 2: du — Drill Down to the Culprit

Once df tells you which filesystem is full, du lets you walk the directory tree by size. The strategy is always to start at the root of the full filesystem and progressively narrow down.

# Start at the top — find the biggest top-level directories
sudo du -sh /* 2>/dev/null | sort -rh | head -15
18G    /usr
 9G    /var
 4G    /home
 2G    /opt
 1.5G  /root
 980M  /lib
 640M  /boot

/var is the most common culprit on servers — it holds logs, package caches, Docker data, databases, and mail queues. Drill in:

# One level into /var
sudo du -sh /var/* 2>/dev/null | sort -rh | head -10

# Then deeper into whatever's biggest
sudo du -sh /var/log/* 2>/dev/null | sort -rh | head -10

# Show two levels at once for a broader view
sudo du -h --max-depth=2 /var 2>/dev/null | sort -rh | head -20

Key du flags worth knowing:

-s              # Summary — one line per argument, no subdirectory listing
-h              # Human-readable (K, M, G, T)
-c              # Print a grand total at the end
--max-depth=N   # Limit recursion depth
-x              # Stay on one filesystem (don't cross mount points)
--exclude=PATTERN  # Skip matching paths

The -x flag is especially useful when / is full but you want to avoid counting /home or /var which are separate mounts:

# Scan only the root filesystem, skip other mounts
sudo du -sh -x /* 2>/dev/null | sort -rh | head -15

Step 3: ncdu — Interactive Disk Explorer

ncdu (NCurses Disk Usage) is an interactive, visual du. It scans a directory tree, then presents a navigable list sorted by size. You can drill into subdirectories, view file counts, and delete files directly from the interface. For manual investigation on a system you're not fully familiar with, it's significantly faster than repeated du invocations.

# Install ncdu
sudo apt install ncdu        # Ubuntu / Debian / Mint
sudo dnf install ncdu        # Fedora / RHEL / AlmaLinux
sudo pacman -S ncdu          # Arch / Manjaro
sudo zypper install ncdu     # openSUSE
sudo emerge app-utils/ncdu   # Gentoo
# Scan the entire root filesystem (may take 1-2 minutes on large disks)
sudo ncdu /

# Scan only your home directory (fast, no sudo needed)
ncdu ~

# Scan /var — the most useful starting point on servers
sudo ncdu /var

# Scan and export results for later review (useful on slow remote systems)
sudo ncdu -o /tmp/disk-report.ncdu /

# Load and browse a saved report
ncdu -f /tmp/disk-report.ncdu

The export/import feature is particularly valuable when scanning a large disk over a slow connection — run the scan, save the report, disconnect, and browse locally at your leisure.

ncdu keyboard shortcuts:

KeyAction
Enter / Open selected directory
/ qGo up one level / quit
dDelete selected item (prompts for confirmation)
sSort by size (default)
nSort by name
CToggle child item count
iShow info panel for selected item
eToggle display of hidden/excluded items
?Help

Common Culprits and How to Clean Them

Systemd Journal Logs

On systems running systemd, the journal can grow very large if it's not configured with a size cap. Check it first.

# Check current journal disk usage
journalctl --disk-usage

# Remove journal entries older than 7 days
sudo journalctl --vacuum-time=7d

# Cap journal to 500 MB total
sudo journalctl --vacuum-size=500M

# Set a permanent cap — edit the journal config
sudo nano /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
SystemKeepFree=1G
MaxRetentionSec=2week
sudo systemctl restart systemd-journald

Package Cache

# Ubuntu/Debian — apt cache in /var/cache/apt/archives/
du -sh /var/cache/apt/archives/
sudo apt clean           # Remove all downloaded .deb files
sudo apt autoclean       # Remove only outdated cached packages
sudo apt autoremove --purge   # Remove orphaned packages and old kernels

# Fedora/RHEL/AlmaLinux
du -sh /var/cache/dnf/
sudo dnf clean all

# Arch
du -sh /var/cache/pacman/pkg/
sudo paccache -r          # Keep last 3 versions per package
sudo paccache -rk1        # Keep only the latest version per package
sudo pacman -Sc           # Remove all uninstalled packages from cache

Docker: Images, Volumes, and Build Cache

Docker is one of the biggest disk consumers on development machines and CI servers. Build cache especially accumulates silently.

# Get a summary of Docker's disk usage
docker system df

# Output:
# TYPE            TOTAL   ACTIVE   SIZE      RECLAIMABLE
# Images          47      12       18.3GB    14.2GB (77%)
# Containers      8       3        1.2GB     900MB (75%)
# Local Volumes   12      4        4.5GB     2.1GB (47%)
# Build Cache     -       -        6.8GB     6.8GB

# Safe prune: stopped containers, dangling images, unused networks, build cache
docker system prune

# Also remove unused volumes — CHECK FIRST what's in them
docker volume ls
docker system prune --volumes

# Remove all unused images (not just dangling ones)
docker image prune -a

# Check the underlying storage directory
sudo du -sh /var/lib/docker/

Old Log Files Outside Journald

# Find large log files
find /var/log -name "*.log" -size +100M 2>/dev/null

# Find uncompressed logs older than 30 days
find /var/log -name "*.log" -mtime +30 2>/dev/null

# Force a log rotation cycle
sudo logrotate -f /etc/logrotate.conf

# Truncate a specific log file safely (don't rm — the process still has it open)
sudo truncate -s 0 /var/log/some-app/large.log

Snap Package Old Revisions

# Snap retains up to 3 old revisions per package by default
du -sh /var/lib/snapd/snaps/

# List all revisions including disabled ones
snap list --all

# Remove all disabled (old) revisions
snap list --all | awk '/disabled/{print $1, $3}' | 
  while read name rev; do
    sudo snap remove "$name" --revision="$rev"
  done

# Set snap to keep only 2 revisions going forward
sudo snap set system refresh.retain=2

npm Cache and node_modules

# npm cache
du -sh ~/.npm/
npm cache clean --force

# node_modules directories are typically the bigger problem
# Find all of them and show sizes
find ~ -name "node_modules" -type d -prune 2>/dev/null | 
  xargs du -sh 2>/dev/null | sort -rh | head -15

# Delete node_modules from old/inactive projects
# (run npm install again when you need them)
find ~/projects/old -name "node_modules" -type d -prune -exec rm -rf {} +

Temporary Files and Trash

# Check temp dir sizes
du -sh /tmp /var/tmp 2>/dev/null

# /tmp is cleared on reboot — safe to clean manually
sudo rm -rf /tmp/*

# Clear your Trash from the CLI
rm -rf ~/.local/share/Trash/files/*
rm -rf ~/.local/share/Trash/info/*

# Find large files anywhere in your home directory
find ~ -type f -size +500M 2>/dev/null | sort

Proactive Monitoring: Get Alerted Before Disks Fill

Reactive disk cleanup is painful. A minimal cron-based alert catches problems before they cause downtime.

sudo tee /usr/local/bin/disk-alert.sh > /dev/null << 'EOF'
#!/bin/bash
THRESHOLD=85
HOSTNAME=$(hostname -f)

df -h | awk -v threshold="$THRESHOLD" -v host="$HOSTNAME" '
NR > 1 && $1 !~ /^tmpfs|devtmpfs|udev/ {
  gsub(/%/, "", $5)
  if ($5+0 >= threshold) {
    printf "DISK WARNING on %s: %s is %s full (%s free of %s)n",
      host, $6, $5"%", $4, $2
  }
}'
EOF
chmod +x /usr/local/bin/disk-alert.sh
# Test it manually
/usr/local/bin/disk-alert.sh

# Add to root's crontab — check daily at 09:00
sudo crontab -e
# Check disk usage every day at 09:00 and email root if over threshold
0 9 * * * /usr/local/bin/disk-alert.sh | mail -s "Disk Alert: $(hostname)" root

For production environments, integrate disk usage metrics into Prometheus with node_exporter and


Go up

This site uses cookies for analytics and advertising (Google AdSense). By continuing to browse, you accept our use of cookies. Learn more