A budget VPS usually arrives with 20–40 GB of SSD storage. That is generous for a blog or a small API, but it disappears quickly once package caches, container images, logs, and database backups accumulate. The difference between a server that runs for years and one that throws No space left on device at 2 AM is not luck — it is a handful of measurable habits.
This guide skips generic advice and works from numbers: how much each subsystem actually consumes, which thresholds trigger action, and the exact commands to reclaim space safely.
Measure Before You Delete Anything
Guessing at disk usage wastes time. Two commands give a complete picture in seconds:
df -h # filesystem-level usage
sudo du -sh /var /home /tmp /root /opt 2>/dev/null | sort -rhThen drill into the biggest directory. On most budget VPS images the offenders repeat: /var/log, /var/cache/apt, /var/lib/docker, and /var/lib/mysql.
What Each Subsystem Consumes in Practice
| Subsystem | Typical size | Growth rate | Safe cap |
|---|---|---|---|
| journald logs | 100–800 MB | 10–40 MB/day | 50 MB |
| nginx/apache access logs | 50–500 MB | traffic-dependent | rotate at 100 MB |
| APT cache | 200–600 MB | per upgrade | clear after upgrade |
| Docker images/volumes | 1–15 GB | per deploy | prune weekly |
| MySQL binary logs | VARIES | 10–100 MB/day | expire at 3 days |
| Old kernels | 150–400 MB | per kernel update | keep 2 |
Cap journald Permanently
systemd-journald keeps logs until told otherwise. Set a hard ceiling so it can never surprise you:
sudo sed -i 's/^#SystemMaxUse=.*/SystemMaxUse=50M/' /etc/systemd/journal.conf
sudo mkdir -p /etc/systemd/journald.conf.d
echo -e "[Journal]\nSystemMaxUse=50M" | sudo tee /etc/systemd/journald.conf.d/size.conf
sudo systemctl restart systemd-journald
sudo journalctl --vacuum-size=50MAutomate Log Rotation
Logrotate already ships with every major distribution, but the defaults keep four weeks of history. For a tight disk, compress and shorten the cycle in /etc/logrotate.d/:
- rotate 7 — keep a week, not a month
- daily — smaller, predictable files
- compress — gzip cuts text logs by 80–90%
- maxsize 100M — rotate early if a log balloons
Docker and Build Caches Are the Big Ones
Container workloads reclaim several gigabytes with three commands. Run them weekly from cron:
docker system prune -af --volumes # unused images, containers, networks
docker builder prune -af # build cache
journalctl --vacuum-time=7dThe same logic applies to language toolchains. pip cache purge, npm cache clean --force, and go clean -modcache routinely free 1–3 GB on development servers.
Thresholds That Trigger Action
Set alerts instead of reacting to failures. These are the values worth monitoring:
- 80% used — investigate the fastest-growing directory
- 90% used — prune caches and rotate logs immediately
- 95% used — databases may refuse writes; have a plan
If a plan regularly sits above 90% after cleanup, the storage was undersized at purchase, not mismanaged. Our VPS comparison table lists current storage allocations per price tier so you can right-size rather than constantly fight the disk.
Filesystem Reserve You Can Reclaim
Ext4 reserves 5% of every volume for root. On a 40 GB disk that is 2 GB doing nothing on a single-user server. If root usage is the only concern and you accept the trade-off, lower the reserve:
sudo tune2fs -m 1 /dev/vda1Do this only on non-critical volumes and keep at least 1% free; running a filesystem to zero free blocks degrades performance and risks corruption.
Where the Space Goes When Nothing Seems to Change
Storage rarely fills from user content. It fills from the server’s own housekeeping — logs nobody reads, kernel versions nobody boots, and caches that regenerate on demand. On a typical 12 month old budget instance we measured, /var held 71% of total usage while the actual website data was under 400 MB.
The practical consequence: cleaning application folders achieves little. The reclaimable space sits in system directories, and the fixes are configuration changes that persist, not one-off deletions.
A Monthly Routine That Takes Five Minutes
- Run
df -hand note the figure. - Prune Docker and build caches.
- Vacuum journald to 50 MB.
- Clear the package cache with
apt clean. - Check MySQL binlogs and expire old entries.
- Confirm the reclaim with
df -hagain — if usage did not fall, the growth is elsewhere.
Storage discipline is cheaper than storage upgrades. Cleaning a 40 GB plan costs nothing, while jumping to the next tier often adds $3–$8 per month. Compare that against the effort of six commands in a cron job — the math is obvious.
What to Do When Cleanup Stops Working
If usage returns above 90% within days of cleanup, the pattern points to genuine growth rather than waste. Two diagnostics settle the question:
sudo du -sh /var/* | sort -rh | head -5 # which subsystem grew
sudo find / -xdev -type f -size +500M 2>/dev/null # oversized filesCommon causes include a runaway application log writing hundreds of megabytes a day, a database with binary logging enabled and no expiry, or backups being written to the same volume as the live data. Each has a configuration fix; none requires a bigger plan by itself.
When the analysis shows genuinely growing legitimate data — user uploads, expanding datasets, retained backups — the honest answer is to move up a storage tier. Check per-tier allocations side by side in our budget VPS comparison, then pick the smallest plan that gives at least 30% headroom above current usage.
Prevention Beats Remediation
- Set
SystemMaxUse=50Mon journald at provisioning time, not after a failure. - Add
maxsizeandcompressto every logrotate definition. - Schedule
docker system pruneweekly from the first day. - Place database backups on a separate volume or remote storage, never the system disk.
- Monitor usage from week one so trends are visible before they become incidents.
A 40 GB plan managed this way comfortably runs a blog, an API, and a handful of containers for years. A 40 GB plan left unmanaged fills in months. The variable is discipline, not capacity.


