The Symptom: Your Server Says “No Space Left on Device”

You try to update a package, upload a file, or start a service, and you get it: No space left on device. Maybe SSH won’t even let you log in properly. Your monitoring dashboard shows a disk that’s 98% full, and you’ve got no idea what’s eating all the space.

We see this at Canadian Web Hosting all the time. A server runs fine for months, then suddenly — disk full. The good news? In most cases, the fix is straightforward once you know where to look. This guide walks you through the systematic process we use to diagnose and resolve disk space issues, from the quick fix that works 60% of the time to the deep-dive cleanup that keeps your server healthy long-term.

This guide applies to any Linux server — Ubuntu, Debian, CentOS, Rocky Linux, or AlmaLinux — whether it’s running on a CWH Cloud VPS, a dedicated server, or even your local lab machine.

Quick Fix: Clear Package Cache and Old Logs

In our experience, about 60% of “disk full” alerts on VPS servers are caused by just two things: bloated package caches and unrotated log files. Here is the fastest way to free up space:

# Clear APT package cache (Debian/Ubuntu)
sudo apt clean
sudo apt autoremove --purge -y

# Or for YUM/DNF (CentOS/Rocky/AlmaLinux)
sudo dnf clean all
sudo package-cleanup --old-kernels --count=2 -y

# Truncate large log files (don't delete, just empty them)
sudo journalctl --vacuum-time=7d
sudo find /var/log -name "*.log" -type f -size +100M -exec truncate -s 0 {} ;

# Check how much space you just freed
df -h /

Run those commands and check your disk usage. If you freed up 10-40% of your disk, you may be done with the immediate crisis. But read on anyway — the root cause section will help you set up automatic log management so this doesn’t keep happening.

If that didn’t free enough space, or if df -h still shows a nearly-full disk, move to the systematic diagnosis below.

Root Causes: Systematic Diagnosis

1. Docker Containers and Images (Most Common on Modern VPS)

If you run Docker on your server, this is the #1 disk culprit. Docker stores images, containers, volumes, and build cache — and none of it gets cleaned up automatically.

How to confirm:

# Check Docker disk usage
docker system df

# This shows you: Images, Containers, Local Volumes, Build Cache
# Example output:
# TYPE              TOTAL     ACTIVE    SIZE      RECLAIMABLE
# Images            12        5         8.4GB     3.2GB (38%)
# Containers        18        3         2.1GB     1.8GB (85%)
# Local Volumes     7         4         1.5GB     500MB (33%)
# Build Cache       —         —         4.2GB     4.2GB (100%)

How to fix:

# Review reclaimable Docker space firstndocker system dfnn# Prefer selective cleanup first:
# Remove dangling images only
docker image prune -f
# Remove stopped containers
docker container prune -f
# Remove build cache
docker builder prune -f

# Check what you freed
df -h /

docker system prune -a is the nuclear option — it removes everything not associated with a running container. On development servers or CI machines, this can free 5-20GB or more. Be aware that this removes build cache, so your next docker compose build will run from scratch.

Prevention: Add this to a weekly cron job:

# /etc/cron.weekly/docker-usage-report
#!/bin/bash
docker system prune -a --volumes >> /var/log/docker-cleanup.log 2>&1

2. Database Growth (MySQL/MariaDB/PostgreSQL)

Databases grow silently. Application logs, session tables, and archived data can balloon over months. MySQL binary logs alone can consume gigabytes if not rotated properly.

How to confirm:

# Find where your database stores data
sudo du -sh /var/lib/mysql   # MySQL/MariaDB
sudo du -sh /var/lib/postgresql  # PostgreSQL

# Check MySQL binary log size
mysql -e "SHOW BINARY LOGS;" 2>/dev/null
# Or check variables
mysql -e "SHOW VARIABLES LIKE 'expire_logs_days';"

# Check each database size
mysql -e "
SELECT table_schema AS 'Database',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
FROM information_schema.tables
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC;
"

How to fix:

# Purge old binary logs (keep only last 3 days)
mysql -e "PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;"

# Set automatic binary log expiration
mysql -e "SET GLOBAL expire_logs_days = 3;"
# Make permanent: add to /etc/mysql/mysql.conf.d/mysqld.cnf:
# expire_logs_days = 3

# For PostgreSQL: vacuum and analyze
sudo -u postgres vacuumdb --all --analyze

If a single application table is gigabytes in size, investigate whether old data can be archived or trimmed. WordPress wp_options tables and logging tables are common offenders.

3. Log File Bloat (syslog, Nginx, Apache, Application Logs)

Even with log rotation configured, errors can produce logs faster than rotation can handle. A single misbehaving application can write gigabytes of error logs to /var/log in hours.

How to confirm:

# Find the largest files in /var/log
sudo du -sh /var/log/*/ /var/log/* | sort -rh | head -10

# Check current log sizes in human-readable format
sudo find /var/log -type f -size +100M -exec ls -lh {} ; 2>/dev/null

How to fix:

# Truncate the largest offenders
sudo truncate -s 0 /var/log/syslog
sudo truncate -s 0 /var/log/nginx/access.log
sudo truncate -s 0 /var/log/nginx/error.log

# For Apache
sudo truncate -s 0 /var/log/apache2/access.log
sudo truncate -s 0 /var/log/apache2/error.log

# Set journald size limits
sudo sed -i 's/#SystemMaxUse=/SystemMaxUse=500M/' /etc/systemd/journald.conf
sudo sed -i 's/#MaxRetentionSec=/MaxRetentionSec=1week/' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald

If a single log file is >1GB, find the application causing it and fix the root issue — otherwise it will fill up again.

4. Unused Kernel Packages

Every kernel update leaves the old kernel installed. After a year of updates, you can have 5-10 old kernels taking up 500MB each — that is 2.5-5GB of wasted space.

How to confirm:

# Check installed kernels (Debian/Ubuntu)
dpkg --list | grep linux-image

# Check installed kernels (CentOS/Rocky)
rpm -qa | grep ^kernel-

How to fix:

# Debian/Ubuntu: autoremove removes old kernels
sudo apt autoremove --purge -y

# Check if any remain
dpkg --list | grep linux-image | grep -v $(uname -r)

# CentOS/Rocky: remove old kernels (keep last 2)
sudo package-cleanup --old-kernels --count=2 -y

5. Application Upload Directories and User Data

Sometimes the culprit is simply that your application grew. User uploads, backup files, temporary exports — they add up.

How to confirm:

# Find the 10 largest directories (from root)
sudo du -x / --max-depth=3 | sort -rh | head -20

# Find files over 500MB anywhere on disk
sudo find / -xdev -type f -size +500M -exec ls -lh {} ; 2>/dev/null

How to fix:

# Archive old backups to offsite storage
# Move large media files to CDN or S3-compatible storage
# Review and delete temporary exports older than 30 days
find /tmp -type f -atime +30 -delete 2>/dev/null
find /var/tmp -type f -atime +30 -delete 2>/dev/null

If your application stores user-uploaded content, consider moving it to object storage (S3-compatible) or a separate data volume. This also makes backups easier — your application data and your OS are on separate disks.

Diagnostic Flowchart: Where to Start

Follow this decision tree to quickly identify the cause:

Step Command If Yes If No
1. Check overall disk usage df -h Root partition > 90% — continue Check other mount points with df -h
2. Find large directories sudo du -sh /* | sort -rh | head -5 /var or /home is biggest — continue Check /tmp, /opt, /usr
3. Check Docker docker system df Docker disk > 5GB — run docker system prune Move to step 4
4. Check logs du -sh /var/log/* | sort -rh | head -5 Any log > 500MB — truncate and fix rotation Move to step 5
5. Check databases du -sh /var/lib/mysql MySQL > 2GB — check binary logs Move to step 6
6. Find largest files find / -xdev -size +500M -ls Found large files — investigate and clean Check inodes with df -i
7. Check inodes df -i Inode usage > 90% — many small files problem Disk is genuinely full — upgrade your plan

Note on inodes: Sometimes df -h shows space available, but you still get “disk full” errors. This means you’ve run out of inodes (file nodes) — too many tiny files have consumed all available file slots. This is common with mail queues, temporary session files, or Node.js cache directories. To find directories with millions of small files:

# Find directories with the most files
sudo find / -xdev -type d | while read dir; do
  count=$(ls -1 "$dir" 2>/dev/null | wc -l)
  if [ "$count" -gt 10000 ]; then
    echo "$count: $dir"
  fi
done | sort -rh | head -10

Prevention: Stay Ahead of Disk Full Alerts

The best way to handle a full disk is to never let it get full in the first place. Set up these three things and you will rarely face an urgent disk crisis:

Automatic Log Management

Ensure logrotate is configured for all your applications, and journald has size limits:

# /etc/logrotate.d/custom
/var/log/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    maxsize 100M
}

Disk Usage Monitoring

Set up a simple cron-based alert that emails you when disk usage exceeds 85%:

#!/bin/bash
# /etc/cron.hourly/disk-alert
THRESHOLD=85
df -h / | awk 'NR==2 {gsub(/%/,"",$5); if($5+0 > '"'"'$THRESHOLD'"'"') system("echo "Warning: Disk at $5%" | mail -s "Disk Alert" admin@example.com")}'

Or better yet, set up CWH Monitoring to alert you automatically — our team monitors thousands of servers and will flag disk issues before they become emergencies.

Regular Cleanup Automation

# /etc/cron.weekly/cleanup
#!/bin/bash
# Docker cleanup
docker system prune -a --volumes 2>/dev/null || true
# Old kernels
apt autoremove --purge -y 2>/dev/null || dnf autoremove -y 2>/dev/null || true
# Journald vacuum
journalctl --vacuum-time=7d
# Clean /tmp
find /tmp -type f -atime +7 -delete 2>/dev/null

When to Escalate

If you’ve gone through all the diagnostic steps above and still can’t free enough space, it may be time to upgrade your plan. A disk that is genuinely full of application data (customer uploads, databases, media files) is not a problem to solve with cleanup — it is a sign that your server has outgrown its storage allocation.

Signs it is time to upgrade:

  • Your application data (databases + uploads) uses >70% of available disk
  • You’ve cleaned everything and are still at >60% usage
  • You are running out of space despite automated cleanup

Canadian Web Hosting makes it easy to upgrade your Cloud VPS storage — our support team can help you migrate to a larger plan with no downtime. If you are on managed support, our team handles disk monitoring and cleanup as part of your plan.

If the issue is caused by a runaway process or a compromised application filling the disk with error logs, our Managed Security team can investigate and resolve the root cause.

Sources and Command Notes

This guide was refreshed in May 2026. The diagnostic commands were smoke-tested in Ubuntu 24.04 for syntax where practical. Destructive cleanup commands are intentionally framed as review steps; confirm backups and service ownership before deleting Docker volumes, database logs, old kernels, or large application files.

Related Issues

Once you have freed up disk space and set up monitoring, consider these related topics:

Related Issues

Ops Note: Free Space Without Deleting the Evidence

When a production server is full, the goal is to restore enough room for services to breathe without deleting the only clue. Start with package caches, old temporary files, and known-safe log rotation. Before removing Docker volumes, database logs, or backups, confirm what service owns them and whether a restore path exists.