Understanding du vs df: Measuring Disk Usage Correctly
What you'll be able to do
- Explain that `df` measures per filesystem while `du` measures per directory
- Pick between `df -h`, `df -i`, `du -sh`, and `du -h --max-depth=1` by purpose
- Name the three causes of a `df` and `du` gap and confirm them with `lsof | grep deleted`
Prerequisites (read these first)
What You'll Learn
- Explain the different roles of
duanddf - Understand why
dfshows full butdutotals do not match - Solve the "I deleted it but space did not return" mystery on your own
- Master a systematic investigation pattern for low-disk incidents
Target Audience: Linux beginners. Anyone who uses du and df by feel.
Words used in this article
- Filesystem: the structure that divides a disk for use. In this article, read it as "a unit such as
/or/homewhose space is counted on its own". - Mount: attaching a disk to a directoryA container that organizes files. Same idea as a "folder" on Windows or macOS. so you can use it. The directory it attaches to is the "mount point".
- inode: the record that holds the management data for one file. The number of these records is limited.
- Process: a running program.
sudo: the instruction that runs a command with administrator rights. You need it to inspect files owned by others. It may ask for a password.- Pipe (
|): the symbol that passes the left command's result to the right command.A | Bmeans "process the result of A with B". grep: the command that searches for text.grep deletedmeans "keep only the lines containing the word deleted".
This article centres on df and du. Both only display information. Neither deletes nor rewrites a file, so running them changes nothing on disk.
The later sections do include commands that delete data or restart services. Those carry their own warnings.
Introduction: Lina's Disk Full Incident
du, but the total does not even come close. What is going on?du and df basically the same thing?The Short Answer
df= free space per filesystem (mount point view)du= usage per directory or file (pathA string that describes the location of a file or directory.-based aggregation)- Mismatches mainly come from deleted-but-open files, mount boundaries, and rootThe special administrator account allowed to do anything on the system.-reserved blocks
A way to picture the two views
Think of a refrigerator. df is the person who looks from the outside and asks how much room is left. du is the person who counts what sits on each shelf.
They look at the same refrigerator, but they count differently. That is why their answers can differ.
df - Free Space Per Filesystem
Conclusion:
dfshows free space per filesystem and returns instantly.
df. It stands for "disk free". It reports total, used, and available space per filesystem./, /home, and a USB drive at /mnt/usb are each counted separately.Try It Out
$ df -h
Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 42G 5.5G 89% / tmpfs 1.9G 0 1.9G 0% /dev/shm /dev/sda2 100G 60G 40G 61% /home
How to Read It
Filesystem: Device name (e.g.,/dev/sda1)Size: Total capacityUsed: Used spaceAvail: Free spaceUse%: Usage percentage (watch carefully above 90%)Mounted on: Mount point
-h do?--human-readable. It shows sizes like 50G or 5.5G instead of raw kilobytes.-h, you get numbers like 52428800. Counting the digits is hard work.Common Options
$ df -h # Human-readable units (G, M, K) $ df -T # Also show filesystem type (ext4, xfs, etc.) $ df -i # Show inode usage instead of byte usage $ df -h /var # Only the filesystem containing this path
Don't Forget df -i
Sometimes you see "No space left" even though there is plenty of byte space. In that case, inode exhaustion may be the cause. It happens when a huge number of tiny files exist.
Make it a habit to check both df -h and df -i.
du - Usage Per Directory
Conclusion:
duwalks files to total usage;-shgives the summary.
du. It stands for "disk usage". It walks the files under the given path and adds up their size.du on a huge directory can take a while.df, which returns instantly.Try It Out
$ du -sh /var/log
1.2G /var/log
Useful Option Combinations
-s(summary): Show only the total-h(human-readable): Friendly units--max-depth=N: Limit recursion to N levels
-sh is the workhorse combo — memorize it.
Find Heavy Subdirectories by Level
$ du -h --max-depth=1 /var
4.0K /var/games 1.2G /var/log 512M /var/cache 24M /var/lib 1.7G /var
Sort by Size
$ du -sh /var/* 2>/dev/null | sort -h
4.0K /var/games 4.0K /var/opt 24M /var/lib 512M /var/cache 1.2G /var/log
Key Points
2>/dev/null: discards permissionThe read / write / execute access rules set on a file or directory.-denied errors from unreadable subdirectoriessort -h: orders sizes with units correctly (it treats1.2Gas larger than512M)
Plain sort -n only looks at the leading number. It would place 1.2G before 512M, so use -h.
The Decisive Difference Between du and df
Conclusion:
dfmeasures filesystems,dumeasures directories.
Comparison Table
| Aspect | df | du |
|---|---|---|
| Unit | Filesystem | Directory / file |
| How it gets data | From the superblock | Walks files directly |
| Speed | Instant | Slow on large paths |
| Deleted-but-open files | Included | Not included |
| Other mounts | Counted separately | Crosses by default (block with -x) |
| Root-reserved blocks | Affects Avail |
No effect |
df says 90% full but du only finds 60% worth of files, that is exactly this gap.Culprit #1: Deleted-But-Open Files
Conclusion: Deleted-but-open files are counted by
dfbut notdu.
df vs du mismatch is this: files that were deleted but are still held open by a process.Lina Gets Stuck: The Log Is Gone but the Space Is Not Back
/var/log/nginx/access.log with rm. It was 500 MB, but df -h shows not a single byte freed. Did I delete it wrong?$ df -h /
Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 42G 5.5G 89% /
rm does not always remove the data right away.du counts by walking names, so it cannot see this data. df counts what the filesystem still has allocated, so it does see it.df and du disagree, I will suspect an open deleted file first.rm as the command that removes a name. The data is released once everyone holding it closes it.Find Deleted-But-Open Files
$ sudo lsof | grep deleted
nginx 1234 root 5w REG 8,1 524288000 ... /var/log/nginx/access.log (deleted) mysqld 5678 mysql 7w REG 8,1 104857600 ... /tmp/ibdata.tmp (deleted)
How to Read It
- Column 1: process name (
nginx,mysqld) - Column 2: PID, the number assigned to a process
- Column 7: size in bytes
- Trailing
(deleted): the flag for files that are unlinked but still open
lsof is short for "list open files". It only lists what is open, so running it changes nothing.
Release the Space
# Restart or reload the holding process $ sudo systemctl restart nginx $ sudo systemctl reload mysql # Advanced: redirect the open fd without restarting # (e.g., truncating /proc/<PID>/fd/<N> to /dev/null — expert territory)
Check Impact Before Restarting
systemctl restart stops the service for a moment. If it fails, the service stays down and stops answering requests.
Before running it on production, confirm two things.
- Whether this is a time window in which the service may stop
- Whether any other service depends on it
To practise safely, use a learning environment or a service whose downtime costs nothing. See No space left on device for the full incident playbook.
Culprit #2: Mount Boundaries
Conclusion:
ducrosses mounts by default; use-xto matchdf.
/home mounted on its own partition. What happens when you run du -sh /?/home in the total, I think.du crosses mount points.df counts each filesystem separately. So du -sh / can come out larger than the number df reports for /.# Stay within one filesystem (matches df scope) $ sudo du -sh -x /
-x (--one-file-system)
This tells du to only aggregate files on the same filesystem as the starting path. That makes the result directly comparable to df.
Culprit #3: Root-Reserved Blocks
Conclusion: ext4 reserves ~5% for root, so
dfAvail looks short.
Avail column of df subtracts that reserved amount. That is why Size - Used does not always equal Avail.# Check the reserved block count $ sudo tune2fs -l /dev/sda1 | grep -i reserved
Reserved block count: 655360 Reserved blocks uid: 0 (user root) Reserved blocks gid: 0 (group root)
Lower the Reservation Carefully
You can drop it with tune2fs -m 1 /dev/sda1. Note that this command rewrites a filesystem setting.
Keep the reservation on the root filesystem. Only consider lowering it on dedicated data partitions.
By contrast, tune2fs -l only lists the settings. It changes nothing.
The Practical Investigation Pattern
Conclusion: Run
df -h, thendf -i, thendu, thenlsofin order.
Disk Investigation Playbook (top to bottom)
- Get the big picture:
df -h(which filesystem is full?) - Check inodes too:
df -i(small-files-exhaustion case) - Find heavy directories:
sudo du -h --max-depth=1 / 2>/dev/null | sort -h - If df and du disagree:
sudo lsof | grep deleted - Trim old logs: Look under
/var/logfor rotated.gzfiles - Won't release?: Restart the holding service with
systemctl restart
Commands for Each Step
# 1. Big picture df -h # 2. Inode check df -i # 3. Drill into heavy directories (one level at a time) sudo du -h --max-depth=1 / 2>/dev/null | sort -h sudo du -h --max-depth=1 /var 2>/dev/null | sort -h sudo du -h --max-depth=1 /var/log 2>/dev/null | sort -h # 4. Open deleted files (sorted by size, biggest first) sudo lsof | grep deleted | sort -k7 -n -r | head # 5. Search by individual large files sudo find / -type f -size +100M 2>/dev/null
Mini Exercises: Try It on Your Box
Conclusion: Three drills: usage, top dirs, and explain the df-du gap.
lsof and --max-depth are not part of the virtual terminalAn interactive program that reads the commands you type and runs them. on this site.sudo is unavailable, stay inside your own home directory.Exercise 1: Show the usage of your / partition.
Show Hint 1 (Direction)
Use the command that looks at free space per filesystem. You do not need to count directories one by one.
Add the option that prints friendly units.
Show Hint 2 (Command name)
Use df. The friendly-unit option is -h. Pass / as the place to inspect.
Show Answer
$ df -h /
Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 42G 5.5G 89% /
The Use% column is the usage of /. Anything above 90% deserves cleanup.
Exercise 2: Find the three largest directories directly under your home.
Show Hint 1 (Direction)
This time use the command that counts per directory. One level deep is enough, so do not descend further.
Then order the result by size and take the last few lines.
Show Hint 2 (Command name)
Use du. Limit the depth with --max-depth=1. Order sizes with sort -h.
Take the last lines with tail. The very last one is the overall total, so take one extra line. Your home directory can be written as ~.
Show Answer
$ du -h --max-depth=1 ~ 2>/dev/null | sort -h | tail -4
64M /home/user/Documents 512M /home/user/Downloads 1.2G /home/user/Videos 2.5G /home/user
sort -h orders from small to large, so taking the tail gives you the largest entries.
The final line is the total for the home directory itself. The three lines above it are the real top three, which is why this takes tail -4.
Exercise 3: Compare df -h / with sudo du -sh -x /, then explain the gap in one sentence.
Show Hint 1 (Direction)
First put the two numbers side by side. Then recall the three causes described in this article.
Some data is counted by only one of the two.
Show Hint 2 (Command name)
Run df -h / and sudo du -sh -x / one after the other. If the gap is large, also run sudo lsof | grep deleted.
Show Answer
$ df -h / $ sudo du -sh -x / $ sudo lsof | grep deleted | head
The three usual reasons are:
- Deleted-but-still-open files (counted only by
df) - Mount boundaries (unless
du -xis used) - ext4 root-reserved blocks (they affect
df'sAvail)
In one sentence: df reports what the filesystem has allocated while du reports what it can reach by name, so data with no name shows up as the gap.
Common Pitfalls
Conclusion: Check sizes with
ls -lhbefore deleting; prefertruncate.
Three Patterns to Avoid
- Running
du -sh /over SSH withoutnohup→ the walk dies if your session disconnects - Deleting files based only on
df→ it has no effect when the cause is an open deleted file rm -rf /tmp/*as a blanket sweep → it corrupts work files of running applications
Safe Habits
- However you write it,
ducounts everything below the path you name. The amount of walking does not shrink - To narrow only the output, use
sudo du -h --max-depth=1 / 2>/dev/null | sort -h. Per-level totals tell you where to dig next - Run long investigations inside
tmuxorscreenso a dropped connection does not kill them - Run
ls -lhfirst to check the size and timestamp before deleting anything - For large logs, prefer
truncate -s 0 logfileoverrm
One note on the third habit. truncate -s 0 empties the file, and the contents cannot be recovered. The file itself survives, so the space is released safely even while a process holds it open.
Look at the size with ls -lh first and confirm the contents are safe to drop.
Review
df counts per filesystem and du counts per directory.sudo lsof | grep deleted first. Remember that order and you will be fine.Today's 3-Line Summary
dfshows free space per filesystem, anddushows usage per directory- The three usual causes of a gap are deleted-but-open files, mount boundaries, and root-reserved blocks
- Investigate in this order:
df -h,df -i,du --max-depth=1,lsof | grep deleted