VPS Disk Management: Expand Storage, Use LVM, Clean Up Space, and Monitor Disk Health
A VPS running out of disk space causes service outages — databases stop writing, Docker containers crash, Nginx can’t write access logs, and applications fail unpredictably. This guide covers proactive disk management: finding what’s consuming space, expanding storage without downtime, setting up LVM for flexible volume management, and configuring alerts before disk fills up.
Step 1: Audit Current Disk Usage
<code"># Disk usage overview
df -hT # Human-readable, with filesystem type
# Find space hogs in a directory
du -sh /* 2>/dev/null | sort -rh | head -15
du -sh /var/* 2>/dev/null | sort -rh | head -10
# Find large files specifically (over 100 MB)
find / -type f -size +100M -not -path "/proc/*" -not -path "/sys/*" \
2>/dev/null | sort -k 5 -rn | head -20
# Docker-specific space usage
docker system df # Shows images, containers, volumes, build cache
# Find log files consuming space
find /var/log -name "*.log" -size +50M | xargs ls -lh
# MySQL/PostgreSQL data directory sizes
du -sh /var/lib/mysql/ /var/lib/postgresql/ 2>/dev/null
Step 2: Clean Up Common Space Hogs
<code"># ── APT package cache ───────────────────────────────
sudo apt autoremove -y # Remove unused dependencies
sudo apt clean # Remove cached .deb packages
sudo apt autoclean # Remove outdated cached packages
# ── Docker cleanup ──────────────────────────────────
docker system prune -f # Remove stopped containers, dangling images
docker image prune -af # Remove ALL unused images (aggressive)
docker volume prune -f # Remove unused volumes (careful!)
docker builder prune -f # Remove build cache
# ── Log compression and cleanup ─────────────────────
sudo journalctl --vacuum-size=500M # Keep only last 500 MB of systemd journal
sudo journalctl --vacuum-time=30d # Or keep only last 30 days
# Compress old logs
sudo find /var/log -name "*.log" -mtime +7 | \
xargs -I{} gzip -9 {} 2>/dev/null
# ── Old kernel versions ─────────────────────────────
# List installed kernels
dpkg -l linux-image-* | grep -E '^ii'
# Remove old kernels (keeps current + one previous):
sudo apt autoremove --purge -y
# ── Temporary files ──────────────────────────────────
sudo rm -rf /tmp/* /var/tmp/*
sudo find /home -name ".cache" -type d | xargs du -sh 2>/dev/null
# ── PostgreSQL VACUUM ──────────────────────────────
sudo -u postgres psql -c "VACUUM FULL;" database_name # Reclaim dead tuples space
Step 3: Expand VPS Disk (Add New Volume)
<code"># After adding a disk via VPS provider dashboard: # 1. Verify new disk appears lsblk # Should show new disk, e.g., /dev/sdb # 2. Partition the new disk sudo fdisk /dev/sdb # Commands: n (new partition) → p (primary) → Enter × 3 → w (write) # Or use sgdisk for GPT: sudo sgdisk -n 0:0:0 -t 0:8300 /dev/sdb # 3. Format the partition sudo mkfs.ext4 /dev/sdb1 # 4. Get the UUID for fstab sudo blkid /dev/sdb1 # 5. Create mount point and mount sudo mkdir -p /mnt/data echo "UUID=YOUR-UUID /mnt/data ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab sudo mount -a # Verify df -h /mnt/data
Step 4: Extend Root Partition (Cloud Volume Resize)
<code"># After resizing the root volume in your VPS dashboard: lsblk # Should show larger disk but same partition size # For LVM-based systems (common on Ubuntu cloud images): sudo pvresize /dev/sda # Resize physical volume to use new space sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv # Extend logical volume sudo resize2fs /dev/ubuntu-vg/ubuntu-lv # Resize filesystem # For non-LVM (direct partition): sudo growpart /dev/sda 1 # Extend partition 1 to fill disk sudo resize2fs /dev/sda1 # Resize filesystem # Verify df -h /
Step 5: Set Up LVM for Flexible Storage
<code"># LVM lets you combine multiple disks and resize volumes without unmounting # Good for setups where you expect to add disks over time sudo apt install -y lvm2 # Create Physical Volume on a disk sudo pvcreate /dev/sdb # Create Volume Group named "data-vg" sudo vgcreate data-vg /dev/sdb # Create Logical Volumes sudo lvcreate -L 50G -n app-lv data-vg # 50 GB for app data sudo lvcreate -L 100G -n db-lv data-vg # 100 GB for database sudo lvcreate -l 100%FREE -n media-lv data-vg # Rest for media # Format logical volumes sudo mkfs.ext4 /dev/data-vg/app-lv sudo mkfs.ext4 /dev/data-vg/db-lv sudo mkfs.ext4 /dev/data-vg/media-lv # Mount them sudo mkdir -p /opt /var/lib/mysql /srv/media echo "/dev/data-vg/app-lv /opt ext4 defaults 0 2" | sudo tee -a /etc/fstab echo "/dev/data-vg/db-lv /var/lib/mysql ext4 defaults 0 2" | sudo tee -a /etc/fstab echo "/dev/data-vg/media-lv /srv/media ext4 defaults 0 2" | sudo tee -a /etc/fstab sudo mount -a # Later: extend a volume when needed (no unmounting required for ext4): sudo pvcreate /dev/sdc # Add new disk sudo vgextend data-vg /dev/sdc # Add to volume group sudo lvextend -l +100%FREE /dev/data-vg/db-lv # Extend DB volume sudo resize2fs /dev/data-vg/db-lv # Resize filesystem online
Step 6: Monitor Disk Usage and I/O
<code"># Real-time disk I/O iostat -x 1 # Extended I/O stats every second iotop # Top-like view of disk I/O by process # Disk usage history (ncdu — ncurses disk usage) sudo apt install -y ncdu iotop ncdu / # Interactive disk usage browser # Check filesystem health sudo tune2fs -l /dev/sda1 | grep "Mount count\|Last checked" sudo fsck -n /dev/sdb1 # Check without fixing (non-destructive)
Step 7: Disk Usage Alerts
<code">sudo nano /usr/local/bin/disk-alert.sh
<code">#!/bin/bash
BOT_TOKEN="YOUR_TELEGRAM_BOT_TOKEN"
CHAT_ID="YOUR_TELEGRAM_CHAT_ID"
THRESHOLD=80 # Alert when disk is over 80% full
df -h --output=pcent,target | grep -v Filesystem | while read usage mount; do
# Remove the % sign
pct="${usage%\%}"
if [ "$pct" -ge "$THRESHOLD" ]; then
MESSAGE="⚠️ *Disk Alert*
Host: \`$(hostname)\`
Mount: \`$mount\`
Usage: ${usage} (threshold: ${THRESHOLD}%)
Time: $(date)"
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d "chat_id=${CHAT_ID}" \
-d "parse_mode=Markdown" \
-d "text=${MESSAGE}" > /dev/null
fi
done
<code">chmod +x /usr/local/bin/disk-alert.sh
# Schedule every hour
echo "0 * * * * root /usr/local/bin/disk-alert.sh" | \
sudo tee /etc/cron.d/disk-alert
# Test immediately
sudo /usr/local/bin/disk-alert.sh
Log Rotation Configuration
<code"># Ensure logrotate is configured for your applications sudo nano /etc/logrotate.d/myapp
<code">/var/log/myapp/*.log {
daily
rotate 14 # Keep 14 days of logs
compress # Gzip old logs
delaycompress # Don't compress yesterday's log (may still be in use)
missingok # Don't error if log file doesn't exist
notifempty # Don't rotate empty files
create 640 www-data www-data # Permissions on new log file
postrotate
systemctl reload myapp 2>/dev/null || true
endscript
}
Getting Started
Disk monitoring should be automated on every VPS from day one. The hourly disk alert script in this guide runs on any Ubuntu VPS at VPS.DO and sends Telegram alerts before storage is exhausted. For growing applications, set up LVM when provisioning so future storage expansion (adding a volume) requires no downtime or partition manipulation.
Conclusion
Proactive disk management prevents the most common cause of unplanned VPS outages — full storage. Automate cleanup of Docker images, APT cache, and old logs; monitor usage with hourly Telegram alerts; and choose LVM for new deployments to make future storage expansion seamless. When a disk does fill up, du -sh /* | sort -rh | head -15 quickly identifies the culprit, and Docker cleanup alone often reclaims 5–20 GB on active deployments.