VPS Disk Full? Find What's Eating Space and Fix It
Your site is throwing 500 errors, SSH logins are getting slow, or MySQL just stopped writing to its tables — and when you check, df -h shows the root partition sitting at 100%. A full disk on a VPS doesn't just stop new files from saving; it can crash MySQL, break cron jobs, and take your whole stack down at once. Here's how to find what's actually eating your space, clear it safely, and stop it from happening again.
Symptom: what a full disk actually looks like
It rarely announces itself cleanly. You'll usually see one or more of these instead:
- WordPress or PHP apps throwing 500 Internal Server Error with no obvious code change
- MySQL refusing to start, or logging
Error writing file (Errcode: 28 - No space left on device) scp/sftpuploads failing partway throughapt installoryum updatedying mid-way- Log rotation failing silently, so logs keep growing until nothing else can
First confirm it's really disk space and not inodes or memory:
df -h # disk space by partition
df -i # inode usage (a full inode table looks the same but df -h can still show free space)
free -h # rule out memory/swap pressure
If / or /var shows 95%+ used, you've found your problem. If df -i shows inodes near 100% but space looks free, you likely have millions of tiny files (session caches, mail queue files) rather than a few huge ones — the fix below still works, just target directories with lots of small files.
Cause: where the space usually goes
On a typical Getwebup VPS running cPanel or a plain LAMP/LEMP stack, the same handful of culprits show up again and again:
| Culprit | Typical location | Why it grows |
|---|---|---|
| MySQL binary logs | /var/lib/mysql | Replication/point-in-time recovery logs that never get purged |
| Application & server logs | /var/log, ~/public_html/wp-content/debug.log | Verbose error logging left on in production, or logrotate not configured |
| Old cPanel/site backups | /backup, /home/*/backups | Daily backups stacking up with no retention limit |
| Package caches | /var/cache/apt, /var/cache/yum | Downloaded .deb/.rpm files never cleared after updates |
| Media uploads & plugin caches | wp-content/uploads, wp-content/cache | Unoptimized images, page-cache files, revision bloat |
| Mail queue | /var/spool/exim or /var/spool/mail | A misconfigured contact form or compromised script spamming outbound mail |
| Docker layers | /var/lib/docker | Dangling images and unused containers left after redeploys |
Fix: find the biggest offenders fast
Don't guess — measure. This one-liner walks from the root down and shows you the heaviest directories first:
du -h --max-depth=1 / 2>/dev/null | sort -rh | head -20
Once you know which top-level directory is heavy (usually /var or /home), repeat one level deeper inside it:
du -h --max-depth=1 /var 2>/dev/null | sort -rh | head -20
Keep drilling down until you land on actual files. For finding individual large files anywhere on disk:
find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -20
1. Clear MySQL binary logs
If you're not running replication, binlogs are usually safe to purge:
mysql -e "SHOW BINARY LOGS;"
mysql -e "PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;"
To stop them piling up again, add an expiry in /etc/mysql/my.cnf:
[mysqld]
binlog_expire_logs_seconds = 259200 # 3 days
2. Trim logs instead of deleting them blindly
Never rm a log file that a running process still has open — the space won't actually free up until the process closes the file handle, and you'll break log rotation. Truncate instead:
truncate -s 0 /var/log/mysql/error.log
truncate -s 0 /home/username/public_html/wp-content/debug.log
Then make sure logrotate is actually running:
logrotate -f /etc/logrotate.conf
3. Clean package caches and old kernels
# Debian/Ubuntu
apt clean
apt autoremove --purge
# AlmaLinux/CentOS
yum clean all
package-cleanup --oldkernels --count=2
4. Purge stale backups
Check /backup and any cpmove-* or .tar.gz archives in /home. Keep the last 2-3 and offload older ones to remote storage (S3, another host) before deleting locally:
find /backup -name "*.tar.gz" -mtime +14 -delete
5. Check for a runaway mail queue
A compromised contact form or plugin can flood the outbound mail queue and fill /var/spool fast:
# Exim (cPanel default)
exim -bpc # count of queued messages
exim -bp | exiqsumm # summary by sender
If you see thousands of messages from one script or one WordPress site, that's your compromise signal — clean up first, then flush the queue.
6. Docker leftovers
docker system df # see what's using space
docker system prune -a --volumes
Only run prune -a once you've confirmed no image or volume in that list is still needed — it deletes anything not attached to a running container.
Prevention: don't end up back here next month
- Set up disk alerts. A simple cron job that emails you at 80% usage beats finding out from an angry client:
*/30 * * * * df -h / | awk 'NR==2{gsub("%","",$5); if ($5+0 > 80) print "Disk at " $5 "%"}' | mail -s "Disk Alert" you@example.com - Cap backup retention. Keep a fixed number of backups and rotate the rest off-server automatically instead of letting them accumulate.
- Rotate logs aggressively. Set
WP_DEBUG_LOGoff in production, and confirmlogrotatecovers every app log, not just the OS defaults. - Set MySQL binlog expiry from day one, especially if you never enabled replication.
- Resize before you're at 100%. If genuine growth (more sites, more media) is the cause rather than log bloat, a Getwebup VPS disk upgrade is a five-minute resize — cheaper than an emergency at 2 a.m.
A full disk is almost always recoverable without data loss if you catch it before MySQL corrupts a table mid-write. Measure first with du/df, clear the obvious log and cache bloat, then put a monitor in place so this is the last time you have to do this the hard way.
Frequently asked questions
Is it safe to just delete large log files with rm?
Only if the process that writes to them is stopped or the file is rotated first. If a running process (like MySQL or Apache) still has the file open, deleting it with rm won't free the disk space until that process restarts or closes the handle — use truncate -s 0 instead, or restart the service after deleting.
df -h shows free space but I still get 'No space left on device'. Why?
You've likely run out of inodes, not disk blocks. Run df -i to check. This happens when a directory fills up with millions of tiny files, such as PHP session files or a mail queue. Find and clear the offending directory rather than looking for large files.
How much free disk space should I keep on a production VPS?
Aim to stay under 80% usage at all times. MySQL, in particular, needs headroom to write temporary tables and sort files during normal operation, and can behave unpredictably when the disk is nearly full.
Will purging MySQL binary logs break anything?
Not if you're not using replication or point-in-time recovery from binlogs. If you do rely on them for backups or a replica server, purge only logs older than your recovery window, and confirm your replica is caught up before purging anything it might still need.
My VPS is genuinely out of room, not just full of junk. What now?
If cleanup only buys you a few days before you're full again, that's growth, not bloat — resize the disk. On Getwebup VPS plans this is a live resize from the control panel in most cases, with no data loss and minimal downtime.