Illustration of a full Linux disk being cleaned up to free space
Linux

Linux Disk Full Fix: Find and Free Space Safely

It always starts with a small, confusing symptom. A deploy fails with No space left on device, MySQL stops accepting writes, a WordPress site shows a white page, or you press Tab in your SSH session and get a strange error about not being able to create a temporary file. Behind all of them is the same problem: the disk is full. The good news is that on a typical VPS the space is rarely taken by your real data. It goes to logs nobody reads, a systemd journal that has been growing for a year, cached packages, old kernels and Docker images from deployments you forgot about. In this guide we find exactly what is using the space, then free it step by step, without deleting anything your server still needs.

Everything below works on Debian 13 and Ubuntu 24.04, whether the server is brand new or has been running for years, and we point out every place where the two distributions behave differently.

Why a full disk breaks a Linux server

A filesystem is the structure that organizes data on a disk partition. On most VPS images it is ext4. It hands out space in small blocks (usually 4 KB each) and keeps track of every file with an inode, a small record that stores the file’s owner, permissions, size and the location of its blocks. A filesystem can run out of either one: out of blocks (no room for data) or out of inodes (no room for new files, even with gigabytes free). Both produce the same error message, which is why we always check both.

Every filesystem is attached to the directory tree at a mount point. The root filesystem is mounted at /, and on a typical VPS almost everything (/var, /home, /usr) lives on it. That is why one runaway log file in /var/log can stop your database in /var/lib/mysql: they draw from the same pool of space.

When the pool is empty, things fail in odd ways. Services cannot write logs or PID files, PHP cannot save sessions so users get logged out, databases refuse transactions or crash, apt cannot download packages, and even your shell may complain, because it writes small temporary files for tab completion and here-documents. SSH logins usually still work, and that is what lets us fix it.

One more detail confuses many people: ext4 reserves about 5% of the space for the root user by default. So df can show Avail 0 and 100% while Used is still a bit smaller than Size. Services running as normal users (www-data, mysql, your app user) hit the wall first, while root can still write a little, which is exactly the margin we need for the cleanup.

What you need

  • A Debian 13 or Ubuntu 24.04 server, such as a fresh DreamServer VPS , with SSH access as a user that has sudo rights.
  • 15 to 30 minutes, more if this is the first cleanup after years of uptime.
  • A recent backup of anything important. We only delete things that can be regenerated, but a backup turns any mistake into a minor inconvenience.
  • Docker is optional: Step 6 only applies if you run containers.
  • If your server is an unprivileged LXC container rather than a KVM VPS, most steps work the same. The exceptions (kernels, bind mounts, filesystem tuning, resizing) are flagged where they come up.

Where the space usually goes on a VPS

Before touching anything, it helps to know the usual suspects. On the small servers we look after every day, the same handful of directories account for almost all of the “mystery” usage. The chart below illustrates a typical small VPS after about a year with a couple of Docker apps. The numbers are approximate and yours will differ, but the order is very often the same.

Approximate space freed on a small VPS after one year (GB)Docker images + cacheDocker container logssystemd journalRotated logsOld kernelsAPT cache~6.5 GB~2.0 GB~1.5 GB~0.8 GB~0.6 GB~0.4 GB01234567Space freed (GB, approximate)Cleanup
Approximate space recovered by each cleanup on a typical small VPS after about a year of use. Illustrative values, your server will differ.

Docker usually wins because every docker compose pull leaves the previous image version behind, and container logs have no size limit by default. The journal comes next, followed by rotated logs, old kernels and the APT package cache. Your own data (websites, databases, uploads) is rarely the problem, and it is the one thing we will not touch.

The linux disk full fix in eight steps

We split the work into phases. Steps 1 to 3 only look: they tell you where the space went without changing anything. Steps 4 to 6 clean the usual suspects. Steps 7 and 8 cover the two special cases that confuse even experienced admins: space held by deleted files, and running out of inodes.

Find (1-3), clean (4-6), special cases (7-8)1Confirmdf -h, df -i2Find big dirsdu -xh3Explorencdu -rx /4Logs, journalvacuum, truncate5APT, kernelsclean, autoremove6Dockerprune, log limits7Deleted fileslsof +L18Inodesdu --inodes
The procedure in eight steps: steps 1 to 3 only look, steps 4 to 6 clean, steps 7 and 8 handle the special cases. The numbers match the step headings below.

If you prefer a decision tree, this is how the steps connect, depending on what df tells you in Step 1:

No space left on deviceStep 1: df -h and df -iBlocks full: Use% near 100%Inodes full: IUse% 100%Steps 2-3: du and ncduStep 8: du --inodesdu total much smallerStep 7: lsof +L1Big folders foundSteps 4-6: clean upMillions of tiny filesfind -mtime -delete
Diagnostic flow: Step 1 tells you whether blocks or inodes ran out, steps 2 and 3 locate the space, then steps 4 to 6, step 7 or step 8 free it.

First aid if the disk is at 100% right now

If the disk is completely full, even installing a diagnostic tool fails, because apt needs space to download. These two commands are safe on any Debian or Ubuntu server and usually give you a few hundred megabytes to work with:

sudo apt clean
sudo journalctl --vacuum-size=100M

The first deletes downloaded package files that are already installed, the second trims the system journal to about 100 MB. Steps 4 and 5 explain both in detail. Now let’s find the real culprit.

Step 1: Confirm what is full with df

df (disk free) lists every mounted filesystem with its size and usage. We hide the virtual ones (tmpfs lives in RAM, squashfs is used by snaps, overlay belongs to Docker) so the output stays readable:

df -hT -x tmpfs -x devtmpfs -x squashfs -x overlay

-h prints human-friendly sizes and -T adds the filesystem type. A server in trouble looks like this:

Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/vda1      ext4   39G   37G     0 100% /
/dev/vda15     vfat  124M   12M  113M  10% /boot/efi

The line that matters is the one with / under “Mounted on”. Notice that 37G used plus 0 available is less than 39G: that is the reserved root space we mentioned. If you have a separate /boot or a data disk mounted at, for example, /srv, check those lines as well. A full /boot is a classic reason for failing kernel updates.

Now check inodes:

df -ih -x tmpfs -x devtmpfs -x squashfs -x overlay
Filesystem     Inodes IUsed IFree IUse% Mounted on
/dev/vda1        2.5M  311K  2.2M   13% /

If Use% is high, continue with Step 2. If IUse% is at or near 100% while Use% looks fine, jump straight to Step 8. On Ubuntu, if you leave out the -x squashfs filter, you will see several /dev/loop devices at 100%. These are read-only snap images that are always “full” by design, so ignore them.

In an unprivileged LXC container the root filesystem may show up under a different name (a ZFS dataset or a loop device, for example) and the inode numbers may look huge or appear as a dash. That is normal, it reflects the storage on the host.

Step 2: Find the big directories with du

du (disk usage) adds up the size of the files under a directory. We ask for the size of each top level directory, stay on the root filesystem with -x (so it skips /proc, /sys and other mounts) and sort the result:

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

2>/dev/null hides “permission denied” noise from special files, sort -h understands sizes like 512M and 2.3G, and tail keeps the largest entries, which end up at the bottom:

16K     /lost+found
1.1G    /home
2.3G    /usr
31G     /var
35G     /

Here /var is clearly the problem, so we drill one level deeper and keep following whatever is largest:

sudo du -xh --max-depth=1 /var 2>/dev/null | sort -h | tail -n 10
410M    /var/cache
2.4G    /var/log
27G     /var/lib
31G     /var

Then /var/lib, and so on, until you reach a directory you recognize, such as /var/lib/docker, /var/log/journal or /var/lib/mysql. To list individual large files across the whole filesystem, find is handy:

sudo find / -xdev -type f -size +200M -exec ls -lh {} + 2>/dev/null | sort -k5 -h

This shows every file larger than 200 MB, sorted by size. Typical finds are forgotten database dumps, old .tar.gz backups in a home directory, huge log files and virtual disk images.

Step 3: Explore interactively with ncdu

Running du over and over works, but ncdu does the same job in an interactive screen that you browse with the arrow keys. It is in the standard repositories on both Debian and Ubuntu:

sudo apt update
sudo apt install -y ncdu
sudo ncdu -rx /

-x keeps it on one filesystem, and -r starts it in read-only mode, which disables deleting. We strongly recommend -r at first: it is far too easy to press d in the wrong directory. After the scan you get a sorted list:

--- / ---------------------------------------------------
   27.1 GiB [##########] /var
    2.3 GiB [          ] /usr
    1.1 GiB [          ] /home

Use the arrows and Enter to open directories, the Left arrow to go back, c to show how many files each directory contains (useful for Step 8), ? for help and q to quit. Write down the paths that surprise you.

Besides the cleanups in the next steps, these are the other usual suspects ncdu tends to reveal:

  • Old backups and dumps in /root, /home/* or /srv: *.sql, *.sql.gz or *.tar.gz files that were meant to be temporary. Copy them off the server if you still need them, then delete them.
  • Crash dumps in /var/lib/systemd/coredump, if systemd-coredump is installed. coredumpctl list shows them, and they can go once you no longer need to debug the crash.
  • User caches such as ~/.cache, ~/.npm or ~/.cache/pip. They are safe to remove and get rebuilt on demand.
  • Database binary logs next to MySQL or MariaDB data. Never delete these files by hand, see the troubleshooting section.

Step 4: Clean logs and the systemd journal

Linux keeps two kinds of logs. The systemd journal is a binary log managed by journald and read with journalctl. Classic text logs live in /var/log and are rotated (renamed, compressed and eventually deleted) by logrotate.

This is where the distributions differ. On Debian 13, rsyslog is not installed by default, so the journal is the main system log, stored persistently in /var/log/journal, and there is no /var/log/syslog. On Ubuntu 24.04, rsyslog is installed too, so most messages are written twice: once to the journal and once to /var/log/syslog. Either setup can fill a small disk if nobody sets a limit.

First, see how big things are:

sudo journalctl --disk-usage
sudo du -sh /var/log/* 2>/dev/null | sort -h | tail -n 10
Archived and active journals take up 1.7G in the file system.

Now trim the journal. --rotate closes the current journal file so that it can be cleaned as well, then --vacuum-size deletes the oldest archived files until the total is under the limit:

sudo journalctl --rotate
sudo journalctl --vacuum-size=200M

The output lists every deleted file and ends with a line like Vacuuming done, freed 1.5G of archived journals. If you would rather keep a time window than a size, use --vacuum-time=2weeks instead.

Vacuuming is a one-time fix. To make the limit permanent, add a small drop-in file rather than editing the main journald.conf (if you already have a file in that directory, merge the setting into it instead of creating a second one):

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

From now on journald keeps roughly 300 MB at most. Next, the text logs. Rotated files end in .1, .2.gz and so on, and they are safe to delete. Always preview first, then run the same command with -delete:

sudo find /var/log -type f \( -name "*.gz" -o -name "*.[0-9]" \) -mtime +7 -print
sudo find /var/log -type f \( -name "*.gz" -o -name "*.[0-9]" \) -mtime +7 -delete

If the problem is a single active log that grew huge, such as a web server access log, do not rm it. The program still has the file open and keeps the space allocated (Step 7 explains why). Empty it in place instead:

sudo truncate -s 0 /var/log/nginx/access.log

Then fix the cause: an application stuck in debug logging, a bot hammering your site, or a missing logrotate rule. sudo logrotate -d /etc/logrotate.conf runs logrotate in debug mode, which changes nothing and shows what it would do, a quick way to check whether your log is covered at all.

Step 5: Clean the APT cache and old kernels

When apt installs or upgrades packages, it keeps the downloaded .deb files in /var/cache/apt/archives. They are not needed once the packages are installed:

sudo du -sh /var/cache/apt
sudo apt clean

apt clean prints nothing when it succeeds. It is always safe, the worst case is that apt downloads a file again later.

Kernels are a bigger win on KVM servers. Every kernel update installs a new kernel package next to the old ones, and each version with its modules takes a few hundred megabytes. First see which kernel is running and which ones are installed:

uname -r
dpkg -l 'linux-image-*' | awk '/^ii/{print $2, $3}'

If the newest installed kernel is newer than what uname -r shows, reboot first so the server actually runs it. On Ubuntu, the file /var/run/reboot-required exists when a reboot is pending. Debian does not always create that file, so on Debian simply reboot if a new linux-image package was installed since the last boot. After the reboot, let apt remove what is no longer needed:

sudo apt autoremove --purge

Read the list before you answer Y. Both Debian and Ubuntu protect the running kernel and the newest one (often one previous version too, as a fallback), so you should only see older linux-image, linux-modules or linux-headers versions and leftover dependencies. Never remove the kernel that uname -r reports.

In an LXC container there are no kernel packages at all, because the container uses the host’s kernel. dpkg -l 'linux-image-*' returns nothing and you can skip this part.

On Ubuntu, snaps add up as well, because snapd keeps older revisions of every snap. You can remove the disabled revisions and tell snapd to keep fewer of them in the future:

snap list --all | awk '/disabled/{print $1, $3}' | while read -r name rev; do sudo snap remove "$name" --revision="$rev"; done
sudo snap set system refresh.retain=2

Step 6: Clean up Docker leftovers

If you use Docker, start with its own accounting:

docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          23        6         9.8GB     7.1GB (72%)
Containers      7         6         210MB     12MB (5%)
Local Volumes   9         5         3.4GB     1.2GB (35%)
Build Cache     41        0         2.3GB     2.3GB

The RECLAIMABLE column is what you can get back. Clean in this order, each command asks for confirmation:

docker container prune
docker image prune -a
docker builder prune

docker container prune removes stopped containers, so if you have a stopped container you plan to start again, skip it. docker image prune -a removes every image not used by an existing container (without -a it only removes untagged “dangling” images). docker builder prune clears the build cache. Each command ends with a Total reclaimed space line.

Be careful with volumes, because that is where container data lives: databases, uploads, configuration. Since Docker Engine 23, docker volume prune only removes unused anonymous volumes, while docker volume prune -a also removes named ones. Run docker volume ls and know what every volume holds before pruning anything, and never delete files under /var/lib/docker by hand: always go through docker commands.

The other big Docker eater is container logs. By default each container logs to a JSON file with no size limit. Find the largest ones (the sh -c is needed because only root can read that directory, so the wildcard has to expand as root):

sudo sh -c 'du -h /var/lib/docker/containers/*/*-json.log' | sort -h | tail -n 5
docker ps -a --no-trunc --format '{{.ID}}  {{.Names}}'

The long ID in the path tells you which container it is. As an emergency measure you can empty a log in place with sudo truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log. The permanent fix is a limit in /etc/docker/daemon.json. If that file already exists, merge these keys into it instead of overwriting it:

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

Validate and apply it:

sudo dockerd --validate --config-file=/etc/docker/daemon.json
sudo systemctl restart docker

The first command should print configuration OK. Restarting Docker briefly restarts your containers, so do it during a quiet moment. The new limit only applies to containers created after the change, so recreate them from each Compose project directory with docker compose up -d --force-recreate. You can also set the same limit per service in compose.yaml:

services:
  app:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

Two more notes. On newer Docker installs that use the containerd image store, image data may live under /var/lib/containerd instead of /var/lib/docker, and the docker commands above clean it either way. And if you run Docker inside an LXC container, check docker info --format '{{.Driver}}': if it says vfs, every image layer is a full copy, which can use several times more space than overlay2.

Step 7: Find deleted files that still use space

Here is the case that surprises everyone: df says the disk is full, but the totals from du or ncdu are much smaller. Usually the gap is made of files that were deleted while a program still had them open.

On Linux, deleting a file only removes its name from the directory. The space is released when the last program holding the file open closes it. So if someone runs rm on a 10 GB log while nginx or an application is still writing to it, the name disappears and du no longer counts it, but the blocks stay allocated until that process restarts. lsof lists these files:

sudo apt install -y lsof
sudo lsof -nP +L1

+L1 means “files with fewer than one link”, in other words deleted ones:

COMMAND  PID     USER   FD   TYPE DEVICE   SIZE/OFF NLINK   NODE NAME
nginx   1234 www-data    5w   REG  252,1 8589934592     0 131090 /var/log/nginx/access.log (deleted)

SIZE/OFF is in bytes, here 8 GB. The cleanest fix is to restart or reload the service holding the file:

sudo systemctl restart nginx

If you cannot restart it right now (a busy database, for example), you can empty the deleted file through the process’s file descriptor. The PID is 1234 and the FD column says 5w, so the descriptor number is 5:

sudo truncate -s 0 /proc/1234/fd/5

Run df -h / again and the space is back. In an LXC container lsof only sees the container’s own processes, which is exactly what you need.

Step 8: Fix inode exhaustion

If Step 1 showed IUse% at 100%, you do not have too much data, you have too many files. Every file, however small, uses one inode, and on ext4 the number of inodes is fixed when the filesystem is created. Find the directory with the most files:

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

Drill down exactly as in Step 2, into /var, then /var/lib, and so on. In ncdu, press C (capital) to sort by number of items. Typical culprits are PHP session files in a custom session directory, a mail queue full of undeliverable messages in /var/spool/postfix, application cache directories with millions of small files, and temporary files a buggy script never cleans up.

With millions of files, rm * fails with “Argument list too long”. Use find instead, preview first, and only inside a directory you have identified as cache or temporary data:

sudo find /var/www/example/cache -type f -mtime +7 -print | head
sudo find /var/www/example/cache -type f -mtime +7 -delete

Replace the path with your own. For mail queues, use the mail server’s own tools instead of find: with Postfix, sudo postqueue -p shows the queue and sudo postsuper -d ALL deferred drops the deferred mail. Then fix the cause, usually a missing cleanup job, otherwise the inodes fill up again within days.

On Debian and Ubuntu, PHP sessions in the default /var/lib/php/sessions directory are cleaned by the phpsessionclean timer, but only there. If your app stores sessions somewhere else, nobody cleans them unless you do.

Prevent it from happening again

Cleaning is half the job, the other half is hearing about the problem at 85%, not at 100%. If you followed the steps, you already capped the journal (Step 4) and Docker logs (Step 6). Now add a tiny check that warns you early. This script logs a warning for every real filesystem above 85% space or inode usage:

sudo tee /usr/local/bin/disk-check >/dev/null <<'EOF'
#!/bin/sh
# disk-check: log a warning when space or inode usage crosses LIMIT percent
LIMIT=85
for mode in "" "-i"; do
  df -P $mode -x tmpfs -x devtmpfs -x squashfs -x overlay 2>/dev/null |
    awk -v limit="$LIMIT" -v mode="$mode" 'NR > 1 {
      use = $5; sub("%", "", use)
      if (use != "-" && use + 0 >= limit) {
        what = (mode == "-i") ? "inodes" : "space"
        printf "%s %s at %s%%\n", $6, what, use
      }
    }'
done | while read -r line; do
  logger -t disk-check "WARNING: $line"
done
EOF
sudo chmod +x /usr/local/bin/disk-check

Run it every 30 minutes with a systemd timer. Timers work the same way on Debian and Ubuntu and do not depend on cron being installed:

sudo tee /etc/systemd/system/disk-check.service >/dev/null <<'EOF'
[Unit]
Description=Warn when disk usage is high

[Service]
Type=oneshot
ExecStart=/usr/local/bin/disk-check
EOF

sudo tee /etc/systemd/system/disk-check.timer >/dev/null <<'EOF'
[Unit]
Description=Run disk-check every 30 minutes

[Timer]
OnBootSec=5min
OnUnitActiveSec=30min

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now disk-check.timer

Test it with sudo /usr/local/bin/disk-check followed by sudo journalctl -t disk-check. There is no output while everything is below 85%, so lower LIMIT temporarily if you want to see a warning. A warning in the journal only helps if someone reads it, so connect it to the monitoring you already have, for example by adding a curl call to a push monitor URL in the script, or by having your monitoring tool alert on disk usage directly. As a rule of thumb, keep at least 15 to 20% of the disk free: databases and package upgrades need headroom to work.

When cleaning is not enough: growing the disk

Sometimes the space really is used by real data, and the right fix is a bigger disk. On a KVM VPS, after the virtual disk has been enlarged (for example by moving to a larger plan), the partition and the filesystem must be extended to use the new space:

lsblk
sudo apt install -y cloud-guest-utils
sudo growpart /dev/vda 1
sudo resize2fs /dev/vda1

lsblk shows the disk and its partitions so you can confirm the names, growpart extends partition 1 of /dev/vda to the end of the disk, and resize2fs grows the ext4 filesystem online, without unmounting. Adjust the device and partition number to what lsblk shows. If your root filesystem is XFS, use sudo xfs_growfs / instead of resize2fs, and with LVM the procedure is different. Many cloud images grow the root partition automatically at boot, so check df -h after the upgrade first. Take a backup before resizing anything. In an LXC container the resize happens on the host, the new size shows up inside immediately and there is nothing for you to run.

Troubleshooting and common mistakes

“No space left on device” but df shows free space

Check inodes first (Step 8). Then remember that the error refers to the filesystem the program was writing to, which may be a separate /boot, /tmp or data disk, not /. Run df -h /path/that/failed to see the right one. Finally, a service running as a normal user hits the 5% reserved limit while root still sees a little free space.

df and du disagree

That is Step 7 in most cases. If lsof +L1 shows nothing, the space may be hidden under a mount point: files written into a directory before a disk was mounted on top of it are invisible to du. A bind mount of / lets you look underneath (replace /srv with your mount point):

sudo mkdir -p /mnt/rootonly
sudo mount --bind / /mnt/rootonly
sudo du -sh /mnt/rootonly/srv
sudo umount /mnt/rootonly

Bind mounts are not allowed inside unprivileged LXC containers, so this check only works on KVM servers.

The journal vacuum freed almost nothing

You probably skipped journalctl --rotate, so the active file was not eligible. If journalctl --disk-usage mentions /run/log/journal, the journal is kept in RAM and is not your disk problem at all.

The space does not come back after deleting files

A running process still holds them open (Step 7). On btrfs or ZFS, snapshots also keep deleted data around until the snapshot is removed.

The biggest directory is a database

Never delete files inside /var/lib/mysql or /var/lib/postgresql. For MySQL or MariaDB binary logs, connect with sudo mysql (socket authentication, the default for MariaDB on Debian and Ubuntu) or with credentials in an option file such as ~/.my.cnf, never with -p<password> on the command line, then run PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY; and set binlog_expire_logs_seconds so it does not happen again. For PostgreSQL, a huge pg_wal usually means a stuck replication slot or a failing archive command: investigate the cause instead of deleting WAL files.

Should I lower the ext4 reserved space?

You can see it with sudo tune2fs -l "$(findmnt -no SOURCE /)" | grep -i 'reserved block count'. Leave the 5% on the root filesystem, it is your safety margin. On a large data-only disk, lowering it to 1% with sudo tune2fs -m 1 /dev/vdb1 is reasonable. This is not possible inside an LXC container.

Security notes

  • Preview before you delete. Use -print before -delete, start ncdu with -r, and avoid rm -rf with variables or wildcards as root. A stray space in a path turns a targeted delete into a disaster.
  • Logs are evidence. A disk that suddenly fills up can be a sign of compromise: a brute-force flood in the SSH logs, a spam script stuffing the mail queue, a crypto miner dropping files in /tmp. Before deleting suspicious logs, archive them and copy them off the server (replace the path with the logs you care about):
sudo tar -czf ~/logs-evidence.tar.gz /var/log/nginx
sudo chown "$USER": ~/logs-evidence.tar.gz

The chown matters because an archive created with sudo is owned by root, and your normal user could not copy it with scp otherwise. On Debian 13, where SSH logins go to the journal, export them with sudo journalctl -u ssh --since "2 days ago" > ~/ssh-journal.txt. Copy the files to your computer, then remove them from the server.

  • Stay out of system databases. Never delete inside /var/lib/dpkg, /var/lib/docker or database data directories. Use apt, docker and the database’s own commands.
  • Back up volumes before pruning. A named Docker volume removed by mistake is gone, there is no recycle bin.
  • Cap logging, do not disable it. Turning logs off to save space leaves you blind during the next incident.
  • Work as your sudo user, not in a permanent root shell, so every destructive command is a deliberate sudo.

So, is your disk under control?

You now have a repeatable routine for any linux disk full fix: confirm with df, locate with du and ncdu, clean the journal, logs, APT cache, old kernels and Docker leftovers, and check the two special cases, deleted-but-open files and inode exhaustion. With journald and Docker logs capped and a small disk check running every 30 minutes, the next “No space left on device” should arrive as an early warning, not as an outage.

If you are outgrowing your current disk, a VPS with us comes with room to grow and can be upgraded when your data does. And if you would rather not chase disk usage at all, our server administration team can set up log limits, monitoring and cleanups for you and keep an eye on them.

FAQ

Is it safe to delete files in /var/log?

Rotated and compressed files (.1, .gz) are safe to delete. Active log files should be emptied with truncate -s 0, not removed with rm, otherwise the space stays in use until the program restarts. Never delete the /var/log/journal directory by hand, use journalctl --vacuum-size instead.

How much free space should a Linux server keep?

Aim for at least 15 to 20%. Databases need room for temporary tables and transaction logs, and package upgrades unpack files before replacing the old ones. A server permanently above 90% will eventually fail at the worst possible moment.

Does apt clean or apt autoremove break anything?

apt clean only deletes downloaded package files, so it is always safe. apt autoremove --purge removes packages nothing depends on anymore, including old kernels, while protecting the running one. Read the list it shows before confirming, especially if you installed software manually.

Why does df show 100% when du shows much less?

Most often a process still holds a deleted file open, see Step 7 and lsof +L1. Less often, files are hidden under a mount point, or btrfs or ZFS snapshots keep deleted data.

Can I delete /var/lib/docker/overlay2 to free space?

No. Deleting files there by hand corrupts Docker’s view of images and containers. Use docker system df to see what is reclaimable and the docker ... prune commands from Step 6 to free it safely.

Trusted By & Member Of

We are proud members of leading internet infrastructure organizations.

RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX