sz

Sejan

Blog

How to Safely Free Up 20GB+ of Disk Space on a Linux VPS: Docker, Build Caches, Package Managers, and Systemd Journals

Home › DevOps & Cloud › How to Safely Free Up 20GB+ of Disk Space on a Linux VPS: Docker, Build Caches, Package Managers, and Systemd Journals
How to Safely Free Up 20GB+ of Disk Space on a Linux VPS: Docker, Build Caches, Package Managers, and Systemd Journals

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 -h may show 40% free disk space while writes still fail. Always verify df -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 -a will 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.

Comments (0)

Leave a Reply ↓

No comments yet. Be the first to start the discussion!

Leave a Comment