Understanding du vs df: Measuring Disk Usage Correctly

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 du and df
  • Understand why df shows full but du totals 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 /home whose 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 | B means "process the result of A with B".
  • grep: the command that searches for text. grep deleted means "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

Lina: Linny-senpai, big problem. The server's disk suddenly hit 100%.
Lina: I checked the largest files with du, but the total does not even come close. What is going on?
Linny-senpai: That is the classic "du vs df mismatch". Surprisingly few people understand it properly.
Lina: Wait, aren't du and df basically the same thing?
Linny-senpai: They look similar. But they measure different things. Let's walk through the difference and why the numbers diverge.

The Short Answer

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: df shows free space per filesystem and returns instantly.

Linny-senpai: Let's start with df. It stands for "disk free". It reports total, used, and available space per filesystem.
Lina: Per filesystem?
Linny-senpai: Per mounted location, in plain words. /, /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 capacity
  • Used: Used space
  • Avail: Free space
  • Use%: Usage percentage (watch carefully above 90%)
  • Mounted on: Mount point
Lina: What does -h do?
Linny-senpai: It is short for --human-readable. It shows sizes like 50G or 5.5G instead of raw kilobytes.
Linny-senpai: Without -h, you get numbers like 52428800. Counting the digits is hard work.
Lina: True, that number tells me nothing at a glance.

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: du walks files to total usage; -sh gives the summary.

Linny-senpai: Next is du. It stands for "disk usage". It walks the files under the given path and adds up their size.
Lina: Walks the files. Does it really visit each one?
Linny-senpai: It does. So du on a huge directory can take a while.
Linny-senpai: That is the opposite of 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
Lina: Oh, that is very useful.
Linny-senpai: It is. And if you sort by size on top of that, the biggest directories stand out at once.

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

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: df measures filesystems, du measures directories.

Lina: I am starting to see it. So what exactly is the core difference?
Linny-senpai: In one phrase, they look at different layers of the same system. A side-by-side table makes it clear.

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
Lina: Got it. So when df says 90% full but du only finds 60% worth of files, that is exactly this gap.
Linny-senpai: Exactly. There are three main causes. Let's look at them one by one.

Culprit #1: Deleted-But-Open Files

Conclusion: Deleted-but-open files are counted by df but not du.

Linny-senpai: The number one cause of a df vs du mismatch is this: files that were deleted but are still held open by a process.
Lina: Deleted but still held open? How is that possible?

Lina Gets Stuck: The Log Is Gone but the Space Is Not Back

Lina: Earlier today I removed /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% /
Lina: The file is gone from the listing, yet usage is still 42G. Is it really deleted?
Linny-senpai: You deleted it correctly. On Linux, rm does not always remove the data right away.
Linny-senpai: While a process still holds the file open, the data stays. Here, the running nginx still has that log open.
Lina: So only the name disappeared and the contents remained. I did not expect that.
Linny-senpai: Right. du counts by walking names, so it cannot see this data. df counts what the filesystem still has allocated, so it does see it.
Lina: That makes sense. When df and du disagree, I will suspect an open deleted file first.
Linny-senpai: Think of 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: du crosses mounts by default; use -x to match df.

Lina: What are the other two causes?
Linny-senpai: One is mount boundaries. Picture /home mounted on its own partition. What happens when you run du -sh /?
Lina: It would include /home in the total, I think.
Linny-senpai: Right. By default, du crosses mount points.
Linny-senpai: But 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 df Avail looks short.

Linny-senpai: The last one is subtle but important. Filesystems like ext4 reserve 5% of capacity for the root user by default.
Lina: Why reserve any at all?
Linny-senpai: So root can still act when regular users fill the disk. It can remove files or run repairs.
Linny-senpai: The 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, then df -i, then du, then lsof in order.

Lina: I understand the differences now. So when someone says the disk is full, what do I actually do?
Linny-senpai: Good question. A fixed playbook keeps you from panicking and breaking things.

Disk Investigation Playbook (top to bottom)

  1. Get the big picture: df -h (which filesystem is full?)
  2. Check inodes too: df -i (small-files-exhaustion case)
  3. Find heavy directories: sudo du -h --max-depth=1 / 2>/dev/null | sort -h
  4. If df and du disagree: sudo lsof | grep deleted
  5. Trim old logs: Look under /var/log for rotated .gz files
  6. 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.

Lina: I have the knowledge now. I want to try it by hand.
Linny-senpai: Good, I prepared three exercises. All of them only display information, so you can run them as often as you like.
Linny-senpai: Run this section on a real Linux machine or WSL, though. 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.
Linny-senpai: If 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 -x is used)
  • ext4 root-reserved blocks (they affect df's Avail)

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 -lh before deleting; prefer truncate.

Three Patterns to Avoid

  1. Running du -sh / over SSH without nohup → the walk dies if your session disconnects
  2. Deleting files based only on df → it has no effect when the cause is an open deleted file
  3. rm -rf /tmp/* as a blanket sweep → it corrupts work files of running applications

Safe Habits

  • However you write it, du counts 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 tmux or screen so a dropped connection does not kill them
  • Run ls -lh first to check the size and timestamp before deleting anything
  • For large logs, prefer truncate -s 0 logfile over rm

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

Lina: Let me sum up. df counts per filesystem and du counts per directory.
Linny-senpai: Right. Same disk, but one looks from the outside and the other counts from the inside.
Lina: My log did not free any space because nginx still had that file open.
Linny-senpai: Well remembered. When the numbers disagree, run sudo lsof | grep deleted first. Remember that order and you will be fine.

Today's 3-Line Summary

  1. df shows free space per filesystem, and du shows usage per directory
  2. The three usual causes of a gap are deleted-but-open files, mount boundaries, and root-reserved blocks
  3. Investigate in this order: df -h, df -i, du --max-depth=1, lsof | grep deleted

Next Reading

Share this article

Next steps