VPS disk full? Find what is using the space

When a VPS runs out of disk, things break in ways that don't look like a disk problem at first. Package installs fail, a database stops accepting writes, Docker can't start a container, and logs show No space left on device. This guide finds the cause with standard Linux tools, frees space safely, and stops the same thing filling the disk again.

The commands assume a systemd-based distribution such as Ubuntu or Debian. Run them over SSH with sudo.

By vpsmon Cloud · Published 10 November 2026

1. Find the full filesystem

df -h -x tmpfs -x devtmpfs -x overlay

Look at Use% and Avail for each mount. The full one is often /, but it can be a separate /var, a data volume, or /boot. Note the mount point, because every later step has to look at the right filesystem.

If Use% is 100% but you can still write as root, the filesystem may be using its reserved blocks. ext4 keeps 5% of the space for root by default, so services running as other users fail first. Free space anyway; the reserve exists so you can log in and fix this.

Also check inodes, which run out separately from space:

df -i -x tmpfs -x devtmpfs -x overlay

If IUse% is at or near 100%, skip to step 5.

2. Find the biggest directories

sudo du -xh --max-depth=1 / 2>/dev/null | sort -h | tail -15

-x keeps du on one filesystem, so mounted volumes and /proc don't pollute the result. Replace / with the mount point from step 1 if a different filesystem is full. Then go one level down into the largest directory and run it again:

sudo du -xh --max-depth=1 /var 2>/dev/null | sort -h | tail -15

Repeat until you reach the files themselves. If ncdu is installed, sudo ncdu -x / does the same thing interactively.

On a small server the space is often under /var, in logs, Docker data, package caches or a database.

3. Clean up the usual causes

The systemd journal

journalctl --disk-usage

By default journald may use up to 10% of the filesystem, capped at 4 GB. On a 25 GB VPS that is 2.5 GB of logs. To trim it now, keeping the most recent 200 MB:

sudo journalctl --vacuum-size=200M

To keep it at that size, create a drop-in file:

sudo mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nSystemMaxUse=200M\n' | sudo tee /etc/systemd/journald.conf.d/size.conf
sudo systemctl restart systemd-journald

Docker images, build cache and container logs

docker system df

This shows space used by images, containers, local volumes and build cache, with a RECLAIMABLE column. Remove stopped containers, unused images and build cache:

docker container prune
docker image prune -a
docker builder prune

docker image prune -a removes every image that no container is using, including ones you may want to start again later. They will be pulled again when needed. Leave volumes alone unless you know what is in them: docker volume prune deletes data that containers are not currently using, and that can include a database.

docker system df does not count container logs, and Docker's default json-file log driver has no size limit. In our test, a container printing one line in a loop wrote 600 MB of log in five seconds. Find the largest logs:

sudo sh -c 'du -h /var/lib/docker/containers/*/*-json.log' | sort -h | tail -5

This path is for a standard Docker install. Snap and rootless Docker keep their data elsewhere; docker info --format '{{.DockerRootDir}}' shows where.

To cap logs for new containers, add this to /etc/docker/daemon.json (merge it with any settings already in the file) and restart Docker:

{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

Existing containers keep their old log settings until they are recreated, for example with docker compose up -d --force-recreate.

Package caches and old kernels

On Debian and Ubuntu:

sudo apt-get clean
sudo apt-get autoremove --purge

clean deletes downloaded package files. autoremove removes packages nothing depends on any more, which usually includes old kernels. Read its list before confirming.

Application logs without rotation

If step 2 points to a single large file under /var/log or an application directory, empty it instead of deleting it while the program is running:

sudo truncate -s 0 /var/log/myapp/app.log

If the program keeps writing without append mode, ls may show the old size again after its next write. Most of that file is now empty and takes no space; du -h on the file shows what it really uses.

Deleting a file that a process still has open does not free the space. Step 4 explains why. To stop the file growing again, add a logrotate rule in /etc/logrotate.d/ or configure the application's own log rotation.

4. Space still missing? Check for deleted files held open

If df says the disk is full but du can't find the space, a running process probably still holds a deleted file open. The kernel keeps the data until the last handle closes. This often happens after someone runs rm on a large log.

sudo lsof -nP +L1

Ubuntu cloud images don't always include lsof. Install it with sudo apt-get install lsof, or use the find command at the end of this step. lsof +L1 lists open files with no remaining name on disk. The SIZE/OFF column shows how much space each one holds, and COMMAND and PID show who holds it. Restarting that service releases the space:

sudo systemctl restart myapp

If you can't restart it, you can empty the file through the process's file descriptor. Take the descriptor number from the FD column without its trailing letter, so 4w becomes 4:

sudo truncate -s 0 /proc/PID/fd/4

Without lsof, sudo find /proc/*/fd -lname '*(deleted)' 2>/dev/null shows which processes hold deleted files: the number after /proc/ is the PID, and the last number is the descriptor. It does not show sizes.

5. Check for too many small files

When df -i shows inodes at 100%, writes fail even with gigabytes free. Something has created millions of small files. Find the directory with the most:

sudo du --inodes -x --max-depth=1 / 2>/dev/null | sort -n | tail -10

Follow the largest count down, as in step 2. Common causes are PHP session files, cache directories, mail queues and an application writing one file per request or job. Delete those files with care. If a directory holds a very large number of files, rm * can fail with "argument list too long", and find /path -type f -delete works instead.

6. Stop it happening again

Each cause above has a permanent fix. Cap the journal, cap Docker logs, rotate application logs, and prune old images after deployments. Disk use often climbs a little each day, so compare this week with last week instead of looking only at today's number.

An alert at 80% gives you time to act before writes start failing. If your server can send mail, a cron job that runs df and emails you is enough.

A monitoring agent can also record history and alert you. The free, open-source vpsmon shows disk use for a single server in a local dashboard. vpsmon Cloud is our hosted dashboard. It keeps 30 days of disk history for each server and alerts by email, Slack, Discord or webhook when the fullest writable disk crosses your threshold. The setup guide shows how to connect a server.

How this guide was checked

The commands were run on Ubuntu 24.04 ARM64 with Docker on 10 November 2026. That covered the journal vacuum and drop-in, Docker pruning and a log-flooding container, a deleted 200 MB file held open by a process (found with lsof and /proc, then emptied through its descriptor), and an inode count over 50,000 small files. The Docker log limits were tested as per-container --log-opt settings with the same values, not through daemon.json. apt-get autoremove was run as a simulation only.