Get the Size of a Directory in Linux the Easy Way

A tidy, well-lit night workspace with a computer terminal on screen showing a Linux directory size command

Disk full.

Two words that have ruined more of my Friday afternoons than any bug I've ever shipped. Early in my career, I crashed a server in the middle of a deployment because nobody was watching directory sizes. Not a race condition. Not a memory leak. Just a log directory that grew quietly for months until there was nowhere left to write.

Since then, checking directory size has been a reflex. Here are the commands I actually use, the gotchas that bite everyone eventually, and the one discrepancy between du and df that will confuse you at the worst possible moment.

The Short Version

du -sh /path/to/directory            # total size of one directory
du -h --max-depth=1 /path | sort -hr # which subdirectories are eating the space
ncdu /path                           # interactive explorer (install it first)
df -h .                              # is the filesystem itself full?
I want to...Command
Total size of a directorydu -sh /path
Each subdirectory, biggest firstdu -h --max-depth=1 /path | sort -hr
Explore (and delete) interactivelyncdu /path
Find the biggest files, not directoriesfind /path -type f -exec du -h {} + | sort -hr | head -20
Check overall disk spacedf -h
Analyze a remote serverssh host "ncdu -o- /path" | ncdu -f-

That's 90% of the job. The rest of this post is the other 10%: the flags, the gotchas, and the tools worth installing.

du: The Command That Answers the Question

du (disk usage) walks a directory tree and sums up the space everything takes. The two flags you'll type every time:

$ du -sh /var/log
1.2G	/var/log
  • -s — summary. Just the grand total, not a line per file.
  • -h — human-readable. Sizes in KB, MB, GB instead of raw block counts.

Without -s, du prints every file and subdirectory it walks. On a big tree, that's a firehose.

Run it with sudo on system directories. du can only count what it can read. Run it on /var or /etc without sudo and you'll get a spray of "Permission denied" errors — and a total that's quietly, dangerously wrong.

A few more flags worth knowing:

  • -c — add a grand-total line when you pass several paths: du -shc /var/log /var/lib
  • -x — stay on one filesystem, so you don't wander into /proc, /sys, or mounted volumes
  • --exclude='node_modules' — skip anything matching a pattern
  • --apparent-size — report actual file sizes instead of allocated blocks (matters for sparse files)
  • -L — follow symlinks, which du skips by default (one reason totals sometimes look smaller than expected)

Which Subdirectory Is Eating the Space?

You'll see this recipe in a lot of tutorials:

du -h /var | sort -hr    # technically works, practically a mess

It sorts, sure. But it also lists every nested directory in the entire tree — hundreds of lines where the noise buries the signal. What you almost always want is one level at a time:

$ sudo du -h --max-depth=1 /var | sort -hr
9.4G	/var
4.1G	/var/lib
2.8G	/var/log
612M	/var/cache

Now the culprit is obvious, and you can drill into /var/lib and repeat. On macOS, the flag is -d1 instead of --max-depth=1 (BSD du spells it differently).

Two gotchas that bite everyone:

Hidden directories. Shell globs skip dotfiles, so du -sh * never sees .cache, .npm, or the .git directory quietly holding 8 GB of history. Include them explicitly:

du -sh .[!.]* * 2>/dev/null | sort -hr

Same hunt, but for files instead of directories:

find /var -type f -exec du -h {} + | sort -hr | head -20

ncdu: The One Tool Worth Installing

As much as I love the terminal, piping du into sort gets old when you're actually cleaning house. ncdu (NCurses Disk Usage) is an interactive du, and it's what I reach for on any box I'm spending more than five minutes on:

sudo apt-get install ncdu   # Debian/Ubuntu
sudo dnf install ncdu       # Fedora/RHEL
brew install ncdu           # macOS

Then just run ncdu /var. Arrow keys to navigate, Enter to drill in, d to delete the highlighted file or directory (it asks first), r to rescan. It turns "find the bloat" from a dozen commands into thirty seconds of arrow keys.

The killer trick: analyze a remote server from your local terminal.

ssh production "ncdu -o- /var" | ncdu -f-

That streams the scan from the remote box (where ncdu needs to be installed) into your local ncdu, so you browse a server's disk like it's your own.

If You Like Shiny Things: dust, gdu, and duf

du has been around since the early '70s, and it shows. The modern reimplementations are worth a look:

  • dustdu rewritten in Rust. Fast, and prints a tree with bars and percentages so the big stuff jumps out: dust /var
  • gdu — written in Go, parallelized for SSDs, with an ncdu-style interface. Noticeably quicker on huge trees.
  • duf — a df replacement with output designed for humans who like color.

None of them replace du — it's already installed everywhere, which on a stranger's production server is the only feature that matters. But on your own machines, they're a quality-of-life upgrade.

Why ls Doesn't Answer the Question

A question I get a lot: "Can't I just use ls -lh?" Try it and you'll notice something odd — every directory reports almost exactly the same size:

$ ls -lh
drwxr-xr-x  14 matt  staff   4.0K Jul 19 09:14 projects

projects could hold 40 GB of code and ls would still say 4.0K. That's because ls shows the size of the directory's own entry — the filesystem bookkeeping that lists which files live in it — not the contents. A directory is really just a list of names pointing at inodes; its "size" is the size of that list. Getting the size of everything it points at requires walking the whole tree, which is exactly what du does.

du vs df: When the Numbers Don't Add Up

df is the other half of this story. It reports space for entire filesystems:

$ df -h .
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       100G   91G  4.2G  96% /

df asks the filesystem for its summary totals — instant. du walks the tree and adds things up — slower, but precise about where the space went. Usually they roughly agree. When they don't, it's one of three things:

  1. Deleted files that are still open. A process holding a deleted file keeps its blocks allocated. df counts them; du can't see them, because the file no longer has a name. Classic case: the 20 GB log you deleted while the app still had it open. The space comes back only when the process lets go. Find the culprits with sudo lsof +L1, then restart the offending process.
  2. Data hiding under a mount point. If someone wrote files to /data before a volume was mounted there, those files are shadowed — du / walks right past them while df counts them. Bind-mount the root filesystem somewhere temporary to see what's underneath.
  3. Reserved blocks. ext4 reserves about 5% of the filesystem for root by default. df accounts for it; du never will.

Learn this before 2 a.m. on the night a disk reads 100% full and du swears there's nothing there. It is not a fun puzzle to solve for the first time under pressure.

Scripting It, So the Server Warns You First

A while back I was consulting for a company whose CI/CD pipeline kept failing. The culprit wasn't the code — it was unmonitored log directories slowly eating the build server's disk. The fix wasn't a hero cleanup session; it was a tiny script in cron:

#!/bin/bash
# Yell about any log directory that has grown past 5 GB
limit=$((5 * 1024 * 1024))   # in KB

du -sk /var/log/*/ | while read -r kb dir; do
  if [ "$kb" -gt "$limit" ]; then
    echo "WARNING: $dir is now $((kb / 1024 / 1024)) GB"
    # wire this into Slack, PagerDuty, or email — and archive old logs:
    # find "$dir" -name '*.log' -mtime +30 -exec gzip {} \;
  fi
done

Two details matter here. First, use -sk (kilobytes) for comparisons — parsing "1.2G" strings in a script is fragile, and -sk is POSIX, so it behaves the same on every Linux and macOS. Second, pair it with a df-based check that warns when any filesystem crosses 85% full. Run both from cron daily, and "disk full" stops being a surprise that picks the worst possible moment.

FAQ

How do I find the largest directories on a Linux server? Start at the root and work down one level at a time: sudo du -hx --max-depth=1 / | sort -hr, then repeat inside whatever wins. The -x keeps you on one filesystem so you don't wander into /proc or mounted volumes. If you can install one tool, ncdu / makes the same hunt interactive.

Why does df show more used space than du? Almost always one of three things: deleted files still held open by a process (check sudo lsof +L1), data hidden underneath a mount point, or the filesystem's reserved blocks. The section above walks through each.

Why does ls show 4.0K for every directory? That's the size of the directory's own entry — the list of filenames it contains — not the size of its contents. Only du (or something built on the same idea) walks the tree and totals the contents.

How do I include hidden files and folders? Shell globs skip dotfiles. From inside the directory, use du -sh .[!.]* * 2>/dev/null | sort -hr. ncdu shows hidden entries by default, which is another reason to reach for it.

Does this work on macOS? Mostly. du and df ship with macOS, but use -d1 instead of --max-depth=1, and the GNU-only flags like --exclude and --apparent-size aren't there. brew install ncdu dust duf gets you the extras.

What's the fastest way to scan a huge directory? du stats every file, so millions of small files (node_modules, CI workspaces) take a while. dust and gdu parallelize the work. And ncdu can save a scan (ncdu -o scan.json /var) and reload it later (ncdu -f scan.json) without walking the tree again.

Wrapping Up

The commands are trivial. The habit is what saves you.

du -sh for the quick answer. --max-depth=1 | sort -hr for the hunt. ncdu when it's cleanup time. df when the numbers don't add up. And if you take one thing from this post, take the script: put the check in cron and let the server watch itself, because disks only fill up at the worst possible time.

Matt Watson

Matt Watson

CEO of Full Scale, 4x Founder, Author of Product Driven

Matt Watson is a serial software entrepreneur based in Kansas City and the founder and CEO of Full Scale, which helps companies build offshore development teams fast. He previously founded Stackify, a developer-tools startup, and was an early CTO at VinSolutions. He's the author of Product Driven and hosts the Startup Hustle podcast.

LinkedInXTikTokWikipedia