Running out of disk space on a production or staging Linux VPS is one of the most common causes of unexpected downtime. Databases lock up with no space left on device errors, Docker containers crash when unable to write temporary files, and system updates fail mid-transaction.
When you log into an alert-triggering server, the instinct is often to frantically search for large files or run aggressive deletion commands that risk corrupting active volumes or breaking service configs. In reality, over 80% of wasted VPS storage is consumed by just four safe-to-prune categories: Docker BuildKit caches, dangling container images, language package manager caches (NPM, Pip, Composer, Go), and unbounded systemd journal logs.
In this guide, we walk through a systematic, step-by-step storage audit to safely reclaim 20GB to 50GB+ of disk space without taking down active applications or deleting critical runtime data.
Step 0: Triage and Diagnose Disk Consumption Fast
Before running cleanup commands, understand where the disk pressure is located without locking system I/O:
# 1. Check filesystem mount capacity and available inodes
df -hT -x tmpfs -x devtmpfs
# 2. Inspect root-level directory consumption (top-level breakdown)
sudo du -h --max-depth=1 / 2>/dev/null | sort -hr | head -n 15
# 3. Quick interactive visual inspection with ncdu (excludes external mounts)
sudo apt-get install -y ncdu
sudo ncdu -x /
Pro Tip on Inodes: Running
df -hmay show 40% free disk space while writes still fail. Always verifydf -i. Millions of tiny cache files (such as unpruned session files or npm caches) can exhaust inode allocations even if physical storage remains available.
1. Reclaiming Docker Storage (The #1 Culprit: 10GB+ Build Cache)
Docker is frequently the single largest consumer of server storage. However, many developers only run docker system prune, completely unaware that modern Docker uses BuildKit by default, storing its layer cache in a dedicated builder subsystem that basic prune commands often skip.
Pruning Docker BuildKit Cache (Reclaim 10GB–30GB)
Every time you run docker compose build or docker build, BuildKit caches intermediate layers, snapshot diffs, and build dependencies in /var/lib/docker/buildkit.
# Check how much space Docker builder and objects consume
docker system df
# Output example:
# TYPE TOTAL ACTIVE SIZE RECLAIMABLE
# Images 14 4 6.82GB 5.12GB (75%)
# Containers 5 5 182MB 0B (0%)
# Local Volumes 8 6 4.21GB 1.10GB (26%)
# Build Cache 42 0 11.47GB 11.47GB (100%)
Notice the Build Cache row: 11.47 GB of 100% reclaimable data. To flush all builder cache safely:
# Reclaim all unused BuildKit build cache
docker builder prune -a --force
# Or if using docker-buildx directly
docker buildx prune --all --force
Pruning Dangling and Unused Images
Old deployment images accumulate as tags are overwritten:
# Remove dangling images (untagged images) - 100% SAFE
docker image prune -f
# Remove ALL unused images (images not associated with an active running container)
docker image prune -a -f
Warning: Running
docker image prune -awill delete stopped service images. If you rely on pre-pulled base images for offline builds, only prune dangling images or ensure your deployment scripts pull fresh images.
Safe vs. Dangerous Volume Pruning
# List orphaned volumes (volumes not attached to any container)
docker volume ls -qf dangling=true
# Prune ONLY orphaned anonymous volumes
docker volume prune -f
Never run docker system prune -a --volumes on production unless you are 100% certain no named persistent volumes (such as MySQL, PostgreSQL, or Redis data stores) are detached from temporarily stopped containers.
2. Purging Language & Runtime Build Caches
If your VPS acts as a CI/CD runner, deployment host, or development environment, package managers quietly cache every downloaded tarball, wheel, and binary across user home directories.
NPM Cache (~/.npm) — Reclaim 1GB–5GB
Node Package Manager saves tarball archives of every installed dependency in ~/.npm/_cacache:
# Check size of NPM cache
du -sh ~/.npm 2>/dev/null
# Safely wipe NPM cache
npm cache clean --force
# Verify and optimize cache integrity
npm cache verify
Python Pip Cache (~/.cache/pip) — Reclaim 1GB–4GB
Python's pip keeps cached .whl files and source archives:
# Inspect Pip cache
du -sh ~/.cache/pip 2>/dev/null
# Native purge command (Pip 20.1+)
pip cache purge
# Manual removal if needed
rm -rf ~/.cache/pip
PHP Composer Cache (~/.composer/cache & ~/.cache/composer)
Composer caches zipped GitHub release archives and repository package metadata:
# Check Composer cache size
composer diagnose 2>/dev/null | grep -i cache
# Clear all cached packages and metadata
composer clear-cache
Go Build & Module Cache (~/.cache/go-build & ~/go/pkg/mod)
Go compiler outputs and downloaded modules can consume massive amounts of space over time:
# Clean build cache, test cache, and module cache
go clean -cache -modcache -testcache
# Clear local cache folder if Go CLI is unavailable
rm -rf ~/.cache/go-build
3. Vacuuming Systemd Journal Logs (/var/log/journal)
On modern systemd distributions (Ubuntu, Debian, CentOS, AlmaLinux), systemd-journald stores binary structured logs under /var/log/journal. Without explicit rotation policies, journal logs can grow up to 10% of the entire root filesystem (often 4GB to 10GB+).
Inspecting and Vacuuming Existing Logs
# 1. Check current journal disk usage
journalctl --disk-usage
# Example output:
# Archived and active journals take up 4.8G in the file system.
# 2. Retain only the last 3 days of logs
sudo journalctl --vacuum-time=3d
# 3. Or restrict maximum log size to 200 Megabytes immediately
sudo journalctl --vacuum-size=200M
Permanent Fix: Enforcing Journal Size Limits
Prevent journald from ever ballooning again by configuring /etc/systemd/journald.conf:
# Edit journald configuration
sudo nano /etc/systemd/journald.conf
Uncomment and set SystemMaxUse and SystemMaxFileSize:
[Journal]
Storage=persistent
SystemMaxUse=200M
SystemMaxFileSize=50M
MaxRetentionSec=7day
Apply changes without restarting the server:
sudo systemctl restart systemd-journald
4. APT Package Cache & Orphaned Dependencies
On Debian and Ubuntu servers, apt stores every downloaded .deb package archive in /var/cache/apt/archives/.
# 1. Check APT cache size
du -sh /var/cache/apt/archives
# 2. Remove all downloaded .deb package files (100% SAFE)
sudo apt-get clean
# 3. Remove obsolete package archives that can no longer be downloaded
sudo apt-get autoclean
# 4. Remove unused dependencies and old unused kernel headers
sudo apt-get autoremove --purge -y
5. Storage Reclamation Safety Matrix (Quick Reference)
Here is your at-a-glance cheat sheet for safe VPS storage maintenance:
| Target Component | Path / Subsystem | Typical Reclaimed | Safety Level | Recommended Command |
|---|---|---|---|---|
| Docker BuildKit Cache | /var/lib/docker/buildkit |
10GB – 30GB | ✅ 100% Safe | docker builder prune -a -f |
| Docker Dangling Images | /var/lib/docker/image |
3GB – 10GB | ✅ 100% Safe | docker image prune -f |
| Systemd Journal Logs | /var/log/journal |
2GB – 8GB | ✅ 100% Safe | journalctl --vacuum-size=200M |
| NPM Package Cache | ~/.npm/_cacache |
1GB – 5GB | ✅ 100% Safe | npm cache clean --force |
| Python Pip Cache | ~/.cache/pip |
1GB – 4GB | ✅ 100% Safe | pip cache purge |
| Composer / Go Cache | ~/.cache/composer, ~/.cache/go-build |
1GB – 6GB | ✅ 100% Safe | composer clear-cache && go clean -cache |
| APT Package Archives | /var/cache/apt/archives |
500MB – 2GB | ✅ 100% Safe | apt-get clean && apt-get autoremove --purge |
| Docker Unused Volumes | /var/lib/docker/volumes |
Varies (10GB+) | ⚠️ Caution (Check Named Volumes) | docker volume prune -f |
Automating VPS Maintenance with a Weekly Cron Script
To avoid emergency midnight alerts, create an automated weekly cleanup script at /usr/local/bin/vps-storage-cleanup.sh:
#!/usr/bin/env bash
# /usr/local/bin/vps-storage-cleanup.sh
set -euo pipefail
echo "==> Starting VPS Disk Cleanup [$(date)]"
# 1. Vacuum systemd logs over 7 days
journalctl --vacuum-time=7d --vacuum-size=300M
# 2. Clean APT caches & orphaned packages
apt-get clean
apt-get autoremove --purge -y
# 3. Clean Docker build caches and dangling images
if command -v docker &>/dev/null; then
docker builder prune -a -f --filter "until=168h"
docker image prune -f --filter "until=168h"
fi
echo "==> Cleanup finished successfully."
Make it executable and register a cron job:
chmod +x /usr/local/bin/vps-storage-cleanup.sh
# Run every Sunday at 3:00 AM
echo "0 3 * * 0 root /usr/local/bin/vps-storage-cleanup.sh >> /var/log/vps-cleanup.log 2>&1" | sudo tee /etc/cron.d/vps-cleanup
By enforcing automatic log rotation, pruning stale Docker build layers, and periodically flushing language package caches, your Linux servers will stay lean, fast, and free of artificial storage emergencies.