MHOSTMHOST

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 root or a user with sudo — the commands below need administrator privileges.
  • An mHost 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.

bash
df -h

The -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/efi

Look 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):

bash
df -i

If 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.

bash
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:

bash
du -sh /var/* 2>/dev/null | sort -rh | head -n 20

Keep 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:
bash
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:

bash
du -sh /var/cache/apt/archives/

Clear it:

bash
apt clean

apt 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:

bash
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:

bash
du -sh /var/cache/dnf/

Clear it:

bash
dnf clean all

Unlike 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:

bash
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:

bash
journalctl --disk-usage

Example output:

Archived and active journals take up 3.2G in the file system.

Trim the journal down to a target size:

bash
journalctl --vacuum-size=500M

This 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:

bash
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:
bash
cat >> /etc/systemd/journald.conf <<'EOF'
[Journal]
SystemMaxUse=500M
EOF
bash
systemctl 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:

bash
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:

bash
du -sh /var/log/* 2>/dev/null | sort -rh | head -n 20

logrotate 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:

bash
systemctl list-timers | grep logrotate

You don't have to wait — force logrotate to run right now:

bash
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:

bash
cat > /etc/logrotate.d/myapp <<'EOF'
/var/log/myapp/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
}
EOF

This 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:

bash
lsof +L1 2>/dev/null | head -n 20

If lsof isn't installed:

bash
# 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:

bash
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:

bash
docker system df

This prints TYPE (Images, Containers, Local Volumes, Build Cache), TOTAL, ACTIVE, and SIZE/RECLAIMABLE — how much space you could free without losing anything.

Safe cleanup:

bash
docker system prune

This 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:

bash
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.

bash
find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null
  • -xdev — same purpose as -x for du in Step 2: stay on the current filesystem, skipping other mounted partitions (and /proc along with them).
  • -type f — regular files only, not directories or devices.
  • -size +100M — only files larger than 100 MB (change the threshold to, say, +1G if 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:

bash
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.mhost.ee account panel or with mHost support.

The expansion itself happens in the VMmanager 6 panel (for how to log in, see "How to log in to VMmanager 6"):

  1. Open the "Virtual machines" section and select your server.
  2. Click the "Parameters" button.
  3. Go to the "Fine-tuning" section → the "VM disk configuration" tab.
  4. 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:

bash
lsblk
bash
growpart /dev/vda 1

For ext4:

bash
resize2fs /dev/vda1

For xfs, the command takes the mount point, not the device:

bash
xfs_growfs /

If the growpart command isn't available:

bash
# Ubuntu/Debian
apt install cloud-guest-utils

# AlmaLinux/Rocky Linux
dnf install cloud-utils-growpart

If 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 mHost 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".