There’s nothing quite like the panic of a server going down because a disk quietly filled up overnight. It’s happened to me more than once, and it’s exactly why checking disk space is one of the first things I teach anyone getting comfortable with Bash. It sounds like a simple task, but there’s a surprising amount of depth once you get past the basic df command — from monitoring specific directories, to setting up alerts, to automating cleanup when space gets low.
This guide covers everything from beginner-level commands to advanced scripting patterns for tracking and managing disk usage on Linux and Unix-like systems.
Why Disk Space Monitoring Matters
Running out of disk space isn’t just an inconvenience — it can cause databases to crash, logging to silently stop, and applications to throw obscure errors that have nothing to do with the actual root cause. On production servers, disk space issues are one of the most common causes of downtime, and they’re almost always preventable with basic monitoring.
Checking Overall Disk Usage with df
The df (disk free) command is the starting point for almost every disk space check.
df -h
The -h flag means “human-readable,” converting raw byte counts into KB, MB, or GB. Output looks like this:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 32G 16G 67% /
/dev/sda2 100G 85G 10G 90% /data
tmpfs 2.0G 1.2M 2.0G 1% /run
Here’s what each column means:
- Filesystem — the device or partition being reported on.
- Size — the total capacity of that filesystem.
- Used — how much space is currently in use.
- Avail — how much space remains.
- Use% — the percentage used, which is usually the number you care about most.
- Mounted on — the mount point (directory) where that filesystem is attached.
If you only want to check a specific filesystem or mount point, pass its path directly:
df -h /data
Checking Directory and File Sizes with du
While df tells you about entire filesystems, du (disk usage) tells you how much space specific files or directories are consuming. This is what you reach for when df tells you a disk is nearly full, but you need to find out what is actually taking up all that space.
du -sh /var/log
The -s flag summarizes the total instead of listing every subdirectory, and -h again gives human-readable output:
2.3G /var/log
To break this down by subdirectory and find the biggest offenders, drop the -s flag and sort the output:
du -h --max-depth=1 /var/log | sort -rh
This produces something like:
1.2G /var/log/nginx
800M /var/log/mysql
300M /var/log/journal
--max-depth=1limits the recursion to just one level of subdirectories, so you get a clean overview instead of an overwhelming list.sort -rhsorts human-readable sizes in reverse (largest first), so the biggest space hogs show up at the top.
Finding the Largest Files on Your System
Sometimes the problem isn’t a directory full of many small files, but a handful of massive ones — old log files, forgotten backups, or core dumps. Here’s a one-liner I use constantly:
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null
Let’s unpack this:
find /searches from the root directory downward.-type frestricts results to regular files (not directories or symlinks).-size +100Mfilters for files larger than 100 megabytes.-exec ls -lh {} \;runsls -lhon each matching file to show its size in readable form.2>/dev/nulldiscards permission-denied errors so they don’t clutter your output.
For a live, sortable view, you can pipe this into sort:
find / -type f -size +100M -exec du -h {} \; 2>/dev/null | sort -rh | head -20
This gives you the top 20 largest files on the system, sorted from biggest to smallest.
Checking Inode Usage
Disk space isn’t just about bytes — it’s also about inodes, which are the data structures the filesystem uses to track files. If you have millions of tiny files (a common problem with cache directories or mail queues), you can run out of inodes even if you technically still have free disk space.
df -i
Output:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 3276800 512340 2764460 16% /
If IUse% is near 100% while Use% from the regular df -h output looks fine, you’ve got an inode exhaustion problem, and you’ll need to find and remove large numbers of small files rather than a few big ones.
Automating Disk Space Alerts
Manually checking disk space is fine for a personal laptop, but on a server you actually manage, you want automated alerts before things get critical. Here’s a simple monitoring script I’ve used in production:
#!/bin/bash
THRESHOLD=85
EMAIL="admin@example.com"
df -h | awk 'NR>1 {print $5 " " $6}' | while read -r usage mount; do
usage_num=$(echo "$usage" | tr -d '%')
if [ "$usage_num" -ge "$THRESHOLD" ]; then
echo "Warning: $mount is at ${usage_num}% usage" | mail -s "Disk Space Alert: $mount" "$EMAIL"
fi
done
Here’s how it works:
df -h | awk 'NR>1 {print $5 " " $6}'skips the header row (NR>1) and extracts just the usage percentage and mount point columns.- The
while read -r usage mountloop processes each line, reading the two values into variables. tr -d '%'strips the percent sign so we can compare the number using standard integer comparison.- If usage is greater than or equal to the threshold (85% in this case), it sends an email alert using
mail.
You can schedule this to run every 15 minutes with a cron entry:
*/15 * * * * /home/john/scripts/disk_alert.sh
Automating Cleanup When Space Runs Low
Sometimes you want more than just an alert — you want the system to clean up after itself. Here’s a script that clears out log files older than 7 days when disk usage crosses a threshold:
#!/bin/bash
THRESHOLD=90
LOG_DIR="/var/log/myapp"
usage=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$usage" -ge "$THRESHOLD" ]; then
echo "Disk usage at ${usage}%, cleaning old logs..."
find "$LOG_DIR" -name "*.log" -mtime +7 -exec rm -f {} \;
echo "Cleanup complete."
else
echo "Disk usage normal (${usage}%). No action needed."
fi
The key line is find "$LOG_DIR" -name "*.log" -mtime +7 -exec rm -f {} \;, which finds every .log file older than 7 days (-mtime +7) and deletes it. This kind of self-healing script has saved me from late-night pages more than once.
Real-World Use Cases
1. Docker environments. Docker images, containers, and volumes can silently consume huge amounts of disk space. Running docker system df alongside your regular disk checks gives you visibility into container-specific usage, and docker system prune can reclaim space quickly.
2. Database servers. Databases often generate large log or WAL (write-ahead log) files. Monitoring the specific mount point where your database lives, separate from the root filesystem, prevents a bloated log from taking down the whole server.
3. CI/CD build servers. Build artifacts and caches accumulate quickly. A nightly cron job checking du on the build cache directory and clearing anything older than a set number of days keeps things tidy.
4. Backup servers. Since backup servers are, by design, meant to store growing amounts of data, tracking growth trends over time (not just a single snapshot) helps you predict when you’ll need to expand storage.
Best Practices
- Always check both
df -h(overall usage) anddf -i(inode usage) — a full disk can be caused by either. - Set alert thresholds below 100%, ideally around 80-85%, to give yourself time to react before things become critical.
- Automate log rotation using tools like
logrotaterather than relying solely on manualfindandrmcleanup scripts. - Keep an eye on
/tmpand/varspecifically, since these directories tend to accumulate junk over time. - Use monitoring tools like
ncdufor an interactive, navigable view of disk usage when a script isn’t enough.
Security Considerations
- Be extremely careful with automated deletion scripts. A misconfigured
find ... -exec rmcan delete far more than intended if the path or pattern is wrong. Always test with-printinstead of-exec rmfirst to confirm what would be deleted. - Restrict who can run disk-cleanup scripts, especially ones with
sudoprivileges, since a bug in such a script could delete critical system files. - Avoid storing sensitive alert credentials (like email or Slack webhook tokens) directly in scripts; use environment variables or a secrets manager instead.
Troubleshooting Common Issues
df shows 100% used, but du doesn’t add up: This usually means a deleted file is still being held open by a running process. Use lsof | grep deleted to find such files, and restart the offending process to release the space.
Disk usage keeps climbing even after deleting files: Check if you’re deleting the actual files or just symlinks. Also verify you’re not deleting from a bind-mounted directory that isn’t the actual storage location.
Permission denied errors while running du or find: Run with sudo if you have the necessary access, or redirect stderr with 2>/dev/null to suppress noise from directories you can’t read.
Common Mistakes to Avoid
- Forgetting the
-hflag and trying to mentally convert raw byte counts. - Running
duon the entire filesystem without--max-depth, which can take a long time and produce overwhelming output. - Writing cleanup scripts without a dry-run mode, risking accidental data loss.
- Ignoring inode usage and only monitoring byte usage.
FAQs
Q: What’s the difference between df and du? df reports space usage at the filesystem level (how full is this disk/partition), while du reports usage at the file/directory level (how much space does this specific folder use).
Q: Why does df show less free space than I expect? Filesystems usually reserve a percentage of space for root, and journaling filesystems have overhead. Deleted-but-open files (held by running processes) can also account for the gap.
Q: How do I check disk space for a remote server? SSH into it and run the same commands: ssh user@host "df -h".
Q: How can I monitor disk space continuously instead of checking manually? Set up a cron job running a threshold-check script like the one above, or use monitoring tools such as Prometheus with node_exporter for long-term tracking and alerting.
Summary
Disk space monitoring in Bash starts with two commands — df and du — but becomes genuinely powerful once you start scripting alerts, automating cleanup, and tracking inode usage alongside byte usage. Whether you’re managing a single personal server or a fleet of production machines, a few well-placed scripts can catch problems long before they turn into outages.