Running out of disk space
A VPS disk doesn't warn you before it fills up — usually you find out from the fallout: the site starts returning 500 errors, apt/dnf refuses to install packages, MySQL writes Disk full to its log, and No space left on device shows up in command output and system logs. This article covers how to quickly find what's actually eating the space, free it up safely, and what to do once there's nothing left to free.
What you'll need
- SSH access as
rootor a user withsudo— the commands below need administrator privileges. - An ForestsNet Cloud VPS running Ubuntu 22.04/24.04, Debian 11/12, or AlmaLinux/Rocky Linux 8/9 — Steps 1–7 (the command line) are written for these systems; Step 8 (expanding the disk in VMmanager 6) works the same way for Windows servers too.
- Access to the VMmanager 6 panel — you'll need this if there still isn't enough space after cleaning up (Step 8).
ℹ️ Tip: all commands below assume you're root. If you're using a regular sudo account, prefix each command with sudo.
Step 1. Find which partition ran out of space
Start with the big picture: df (disk free) shows how much space is used and available on each mounted partition.
df -hThe -h (--human-readable) flag prints sizes in convenient units (K, M, G) instead of bytes. Example output:
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 40G 38G 1.2G 97% /
tmpfs 2.0G 0 2.0G 0% /dev/shm
/dev/vda15 105M 6.1M 99M 6% /boot/efiLook at the Use% column. On most VPS everything lives on a single / partition, but if /home, /var, or a database has its own separate partition, that's the one that might actually be full — note the exact Mounted on value and run every following step against that partition.
ℹ️ Tip: while you're at it, check the filesystem type too — df -hT adds a Type column (ext4, xfs, etc.). You'll need it in Step 8: the command for growing the filesystem after expanding the disk differs between ext4 and xfs.
A disk can also "run out" without Use% hitting 100% — if you've run out of inodes (the limit on the number of files, not their total size):
df -iIf IUse% there is close to 100% while the actual space looks fine, the problem isn't file size but file count (a typical example is millions of tiny session or cache files). Steps 2–7 below still help in this case too — you find the offending directory the same way and clean it up.
✅ Done: you know the exact partition (Mounted on) where space or inodes ran out. Move on to Step 2.
Step 2. Find out what's actually taking up the space
Next, track down the specific offenders with du (disk usage), drilling down the directory tree level by level.
du -sh /* 2>/dev/null | sort -rh | head -n 20-s(--summarize) — don't list the contents of each directory, just show the grand total for it.-h— sizes in human-readable form.2>/dev/null— hide "permission denied" errors for directories you can't read.sort -rh— sort by descending size.
ℹ️ Tip: you can ignore the /proc and /sys lines in the output — they're virtual filesystems and don't use any real disk space, whatever du happens to report for them.
Take the largest directory from the output and repeat the command inside it:
du -sh /var/* 2>/dev/null | sort -rh | head -n 20Keep drilling down the same way until you see specific files or directories you can actually clean up. Common culprits on a VPS: /var/cache (Step 3), /var/log (Step 5), /var/lib/docker (Step 6), and directories holding backups or database dumps if you keep them on the same disk — see "Scheduled backups with cron" and "Database backups", which also cover moving copies off the server.
ℹ️ Tip: if the server has several partitions (say, a separate /home or /var/lib/docker), the command above lumps them all in with the root partition without distinguishing what physically lives where. To measure only the partition you found in Step 1, swap the asterisk for the -x (--one-file-system) flag on a single directory instead — for /, for example:
du -x --max-depth=1 / 2>/dev/null | sort -rh | head -n 20-x stops du from crossing into other mounted filesystems at all — unlike the asterisk-based command above, this one shows only what actually lives on the / partition, and /proc, /sys, and any other partition (a separate /home, say) come back empty.
ℹ️ Tip: if going directory by directory is tedious, install the interactive ncdu — on Ubuntu/Debian: apt install ncdu; on AlmaLinux/Rocky the package comes from the EPEL repository (dnf install epel-release, then dnf install ncdu). It builds the same du tree but with convenient directory navigation right in the terminal.
✅ Done: you've found specific directories or files you can clean up. What's next depends on what you found: package cache (Step 3), logs (Steps 4–5), Docker (Step 6), or something else (Step 7).
Step 3. Clear the package manager's cache
apt and dnf both keep already-downloaded .deb/.rpm packages on disk — after a few rounds of updates this can add up to a couple of gigabytes.
Ubuntu / Debian
Check how much the cache is using first:
du -sh /var/cache/apt/archives/Clear it:
apt cleanapt clean removes everything from /var/cache/apt/archives/ — apt will simply re-download already-installed packages if it ever needs them again. Also remove dependencies left behind by removed packages that nothing needs anymore:
apt autoremove --purge--purge also deletes the leftover config files (in /etc) of packages that are no longer needed, not just the packages themselves.
AlmaLinux / Rocky Linux
Check the cache:
du -sh /var/cache/dnf/Clear it:
dnf clean allUnlike dnf clean packages (downloaded packages only), dnf clean all also clears the cached repository metadata — dnf will just re-download it on the next command. Remove unused dependencies:
dnf autoremoveℹ️ Tip: if you'd rather not think about this manually, add apt clean or dnf clean all to a weekly cron job — setting up scheduled tasks is covered in "Scheduled backups with cron" (it uses backups as the example, but the cron mechanics are the same).
✅ Done: du -sh on the cache directory now shows megabytes instead of gigabytes.
Step 4. Clean up the systemd journal (journald)
journald — systemd's logging service — stores the journal in binary form, and on some servers it grows over time, especially if some service is logging way more than it should.
Check how much space the journal is using right now:
journalctl --disk-usageExample output:
Archived and active journals take up 3.2G in the file system.Trim the journal down to a target size:
journalctl --vacuum-size=500MThis removes the oldest archived (already rotated) journal files until the total footprint is under 500M — the active file currently being written to isn't touched, so it's safe. Instead of a size, you can cap the journal by age:
journalctl --vacuum-time=30d(keeps only entries newer than 30 days)
⚠️ Important: --vacuum-size and --vacuum-time only have a one-time effect — the journal will start growing again. To make the limit permanent, set it in the config:
cat >> /etc/systemd/journald.conf <<'EOF'
[Journal]
SystemMaxUse=500M
EOFsystemctl restart systemd-journaldℹ️ Tip: if the server has no /var/log/journal directory, the journal is only kept in memory (/run/log/journal) and doesn't survive a reboot — so it isn't using any disk space at all, and you can skip this step. Check with: ls -d /var/log/journal 2>/dev/null — empty output means the directory doesn't exist.
You can turn on persistent journal storage (and the limit from the example above) like this — the command creates the directory and asks systemd-tmpfiles to initialize it; no restart needed, journald picks up the directory on its own:
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal✅ Done: journalctl --disk-usage shows a size within the limit you set.
Step 5. Find and clean up large logs
Check what's actually heavy inside /var/log:
du -sh /var/log/* 2>/dev/null | sort -rh | head -n 20logrotate is already installed and configured on every supported distribution — it runs automatically once a day (via the systemd timer logrotate.timer on newer Ubuntu/Debian, or the classic /etc/cron.daily/logrotate job elsewhere) and compresses and removes old log copies according to the rules in /etc/logrotate.d/. Check which mechanism your server actually uses:
systemctl list-timers | grep logrotateYou don't have to wait — force logrotate to run right now:
logrotate -f /etc/logrotate.conf-f (--force) rotates every configured log immediately, even if it hasn't hit its size or age threshold yet. If one particular log keeps growing and the default rules aren't enough, add a dedicated config for it:
cat > /etc/logrotate.d/myapp <<'EOF'
/var/log/myapp/*.log {
daily
rotate 7
compress
missingok
notifempty
}
EOFThis rule rotates *.log under /var/log/myapp/ daily, keeps the 7 most recent copies, compresses them (compress), and doesn't complain if the file or new entries are missing (missingok, notifempty).
⚠️ Important: never rm a log file that a running process is actively writing to, expecting to free space instantly. On Linux, rm only removes the file's name from the directory — if a process still has it open, the disk stays allocated until that process restarts, and df won't show the space back even though ls no longer shows the file.
Find these "ghost" files — open, but already deleted:
lsof +L1 2>/dev/null | head -n 20If lsof isn't installed:
# Ubuntu/Debian
apt install lsof
# AlmaLinux/Rocky Linux
dnf install lsof+L1 lists open files whose link count is below 1 — exactly the "deleted but still open" case. The COMMAND and PID columns show which process is holding the file, and SIZE shows how much space it's still using. You can free the space without restarting the service by truncating the file through the process's own file descriptor:
truncate -s 0 /proc/<PID>/fd/<FD>Substitute <PID> and <FD> with the values from the lsof output.
⚠️ Important: never do this to a database's write-ahead log, binlog, or any file a process relies on for crash recovery — it will corrupt data. It's safe for plain application and web server logs, where losing the buffered content doesn't matter. Going forward, instead of rm-ing a log that's actively being written to, use truncate -s 0 /var/log/big.log (or : > /var/log/big.log) — it's the same file and the same descriptor, so space is freed instantly with zero risk.
✅ Done: du -sh /var/log/* no longer shows any suspiciously large files.
Step 6. If you're running Docker — clean up unused data
Docker keeps images, stopped containers, unused networks, and build cache under /var/lib/docker (see "Docker and Docker Compose" for details) — on a server running containers, this is often the single biggest consumer of disk space.
See the breakdown by type first:
docker system dfThis prints TYPE (Images, Containers, Local Volumes, Build Cache), TOTAL, ACTIVE, and SIZE/RECLAIMABLE — how much space you could free without losing anything.
Safe cleanup:
docker system pruneThis removes stopped containers, networks with no containers attached, dangling (untagged) images, and unused build cache. It doesn't touch volumes or images that still carry a tag, even if nothing's used them in a while.
To also remove every unused image (not just untagged ones) and every unused volume:
docker system prune -a --volumes⚠️ Important: --volumes deletes anonymous volumes that aren't attached to any container — including database data directories, if the container that used them has already been removed. Before running this flag, make sure you don't need that data, or that you have a copy of it — see "Database backups".
ℹ️ Tip: both commands ask for confirmation (y/N) before deleting anything and list what's about to go. For use in scripts, there's a -f (--force) flag that skips the prompt.
✅ Done: docker system df shows the space has been freed.
Step 7. Find large files individually
Sometimes the space isn't spread across a whole directory but sitting in one or two individual files — a forgotten database dump, a core dump from a crashed process, an ISO uploaded once for a one-off task and never removed.
find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null-xdev— same purpose as-xforduin Step 2: stay on the current filesystem, skipping other mounted partitions (and/procalong with them).-type f— regular files only, not directories or devices.-size +100M— only files larger than 100 MB (change the threshold to, say,+1Gif needed).-exec ls -lh {} \;— print the size and path of each match in human-readable form.
If there are a lot of matches, sort them by size:
find / -xdev -type f -size +100M -printf '%10s %p\n' 2>/dev/null | sort -rn | head -n 20ℹ️ Tip: if data is spread across several partitions (say, a separate disk mounted at /home or /data), drop -xdev — the command will walk every mounted filesystem at once, though it'll be slower and noisier because of /proc.
✅ Done: you've found the large files — delete the ones you don't need, or move them off the server (see "Backups with rsync" and "Incremental backups with restic").
Step 8. If there's still not enough space — expand the disk
If there's still little free space left after all the previous steps, the only option left is to grow the disk itself. Depending on your plan, that means either upgrading the plan as a whole or expanding just the disk without changing the rest of the server's specs — check the exact option for your plan in the my.forestsnet.cloud account panel or with ForestsNet Cloud support.
The expansion itself happens in the VMmanager 6 panel (for how to log in, see "How to log in to VMmanager 6"):
- Open the "Virtual machines" section and select your server.
- Click the "Parameters" button.
- Go to the "Fine-tuning" section → the "VM disk configuration" tab.
- In the Storage field, enter the new, larger disk size in gigabytes — you can only increase the disk from the panel, never shrink it.
📷 screenshot: the "VM disk configuration" tab under VMmanager 6's fine-tuning settings
From here there are two options:
- The "Increase disk size without VM reboot" checkbox is on — the disk grows on the fly, with no server downtime. This only works for ext4, xfs, and ntfs filesystems, and only if the disk is attached through a VirtIO controller.
- The checkbox is off — the resize happens with a server reboot, and you additionally need to specify which partition (Partition) to expand into — VMmanager grows it automatically during the reboot. This method doesn't depend on the disk controller type — use it if the checkbox above is unavailable or didn't work.
After a no-reboot disk expansion, the operating system doesn't see the new space yet — it exists on the disk itself, but not in the partition or the filesystem. Identify the device and partition name (usually already visible in the df -h output from Step 1 — in that example, the root partition is on /dev/vda1, i.e. partition 1 of disk /dev/vda; if unsure, check with lsblk), then expand the partition and the filesystem from inside the OS:
lsblkgrowpart /dev/vda 1For ext4:
resize2fs /dev/vda1For xfs, the command takes the mount point, not the device:
xfs_growfs /If the growpart command isn't available:
# Ubuntu/Debian
apt install cloud-guest-utils
# AlmaLinux/Rocky Linux
dnf install cloud-utils-growpartIf you expanded the disk with a server reboot (checkbox off), check df -h after it comes back up: if the new size is already there, there's nothing more to do; if not, expand the filesystem with the same resize2fs/xfs_growfs commands as above.
Windows: open Disk Management, right-click the partition that now has unallocated space next to it, and choose "Extend Volume".
✅ Done: df -h (on Windows, the disk's properties) shows the new, larger size in the Avail column.
Prevention: set up disk monitoring
Cleaning up a disk by hand after the site has already gone down is a bad strategy. The right approach is to find out about low disk space in advance, from an alert, rather than after the fact from an outage.
Set up Zabbix and connect your server to it — the "Monitoring with Zabbix" article covers installing the server and agent on an ForestsNet Cloud VPS. The standard "Linux by Zabbix agent" template auto-discovers every mounted filesystem and already ships with triggers for low disk space — a warning by default at 80% full and a critical alert at 90% (the {$VFS.FS.PUSED.MAX.WARN} and {$VFS.FS.PUSED.MAX.CRIT} macros). You can adjust these thresholds per partition — for example, raising it for a logs partition where 90% full is normal operating conditions.
ℹ️ Tip: monitoring without alerts just means you'll check it only once the site is already down — after the fact. Set up at least an email or Telegram notification in Zabbix for these triggers.
What's next
If the disk fills up quickly and repeatedly rather than as a one-off, the likely cause is the application itself: logs that keep growing, a cache nobody clears, or backups piling up on the same disk instead of being moved off the server — see "Scheduled backups with cron", "Backups with rsync", and "Incremental backups with restic". If the site didn't come back on its own once space ran out, "My website won't load" and "A service won't start" can help you dig further. And if, on the other hand, there's plenty of disk and resources but the server still crawls, that's the subject of "High server load".