Linux / Bash

Linux Disk Usage High But du Doesn't Show Why

When df reports a disk as nearly full, but adding up everything du finds doesn't come close to explaining the used space, the most common cause is a deleted file that's still being held open by a running process — Linux doesn't actually free that space until every process with the file open closes it.

The Problem

df -h shows a filesystem is almost full:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   47G  1.2G  98% /

But running du to find what's actually using that space adds up to far less:

du -sh /* 2>/dev/null
# totals nowhere near 47G

Why It Happens

This mismatch happens because df reports actual disk block usage at the filesystem level, while du only sees files that still have a visible directory entry — these aren't always the same thing. Common causes:

  • A file was deleted (rm) while a running process still had it open — on Linux, the disk space isn't actually released until every process holding the file open closes it, even though the file no longer appears anywhere in the directory tree that du traverses
  • Log files or temporary files being continuously written to by a long-running process (a database, a web server) that was deleted or rotated incorrectly, leaving the old file's space held by the still-running process
  • A separate mount point or bind mount is nested inside a directory du is scanning, and files under that nested mount are being double-counted or miscounted depending on how du is invoked
  • Reserved filesystem blocks (a percentage historically reserved for root on ext-family filesystems) counting toward "used" space in df's accounting in a way that doesn't correspond to any actual file du could ever find

The Fix

First, check for deleted-but-still-open files, which is the most common cause by far. lsof can list files that are open but have been unlinked (deleted) from the filesystem:

lsof +L1

This flag specifically shows files with a link count of less than 1 — meaning they've been deleted but are still open by some process. The output includes the process holding the file open and the amount of space it's consuming.

Once you've identified the responsible process, you have two options: restart it (which releases the file handle and actually frees the disk space), or, if restarting isn't immediately practical, truncate the file in place without deleting it, which is a common technique specifically for large log files still actively being written:

: > /proc/<pid>/fd/<fd_number>

This truncates the file to zero length while the process continues writing to the same (now empty) file, immediately freeing the space without requiring a service restart — useful specifically for services where restarting isn't convenient during business hours.

To confirm whether nested mount points are affecting your du results, use the -x flag to restrict du to a single filesystem, avoiding double-counting or confusion from mounted subdirectories:

du -sxh /*

To get a clearer overall picture of what's actually consuming space, sorted by size, rather than manually reviewing many separate du outputs:

du -ah / 2>/dev/null | sort -rh | head -20

This lists the twenty largest files or directories found, sorted from largest to smallest, which is usually a faster way to spot an unexpectedly large file than scanning through the full directory tree manually.

Still Not Working?

If lsof +L1 shows nothing but the discrepancy persists, check for reserved blocks on ext-family filesystems, which by default reserve 5% of total disk space for the root user — this reserved space counts as "used" in df's output but will never show up as any file du can find, since it isn't allocated to any file at all:

tune2fs -l /dev/sda1 | grep "Reserved block count"

If this reserved percentage is unnecessarily high for your use case (5% of a very large disk can be a meaningful amount of space), it can be reduced, though this should be done cautiously and isn't relevant to smaller root partitions where the reserved space serves an important purpose in preventing the system from becoming completely unusable if it fills up entirely.