Getting a disk usage alert on a production server is one of those moments that demands immediate action. If the disk fills completely, the website goes down — no logs can be written, no uploads accepted, databases can’t write. So when I see that alert, I follow a systematic chain of commands to find the culprit fast.
This post walks through my exact process.
The Alert
Disk usage at 95%. The site is still up but barely. I have maybe a few hundred megabytes of breathing room before things start breaking.
First thing — don’t panic and don’t start deleting files randomly. Find the cause first.
Step 1: Confirm the Situation with df -h
df -h
Output:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 40G 38G 2.0G 95% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
df shows me disk usage at the filesystem level. The root partition /dev/sda1 is 95% full. Everything else is fine. So I know exactly where to dig — the root filesystem.
Step 2: Find the Heaviest Directory with du
df tells me the filesystem is full. du tells me where the space actually went.
du -h --max-depth=1 /
Output:
128M /home
18G /var
12G /usr
1.2G /opt
38G /
/var at 18GB is the obvious suspect. Everything else is a reasonable size. I drill down.
du -h --max-depth=1 /var
Output:
16G /var/log
1.2G /var/lib
512M /var/cache
18G /var
/var/log is consuming 16GB. Logs have grown completely out of control. One more drill down.
du -h --max-depth=1 /var/log
Output:
12M /var/log/nginx
128M /var/log/mysql
15G /var/log/php8.3-fpm.log
512M /var/log/journal
There it is. php8.3-fpm.log is 15GB. A single log file has consumed most of the disk.
Step 3: Understand What’s Inside the Log
Before deleting or truncating anything, I want to know what caused this. A 15GB log file means something was throwing errors at a massive rate — possibly thousands per second.
tail -n 50 /var/log/php8.3-fpm.log
I use tail not cat. Never use cat on a multi-gigabyte file — it will flood your terminal and tell you nothing useful. tail shows the most recent lines which is exactly what I want.
I see PHP errors repeating thousands of times — in this case the php-fpm worker pool was exhausted and each failed request was being logged. This is the root cause. Fix that and the logging stops.
Step 4: Free the Disk Space Safely
Now I need to clear the log file. The key word is truncate, not delete.
Why not delete? If a process (php-fpm in this case) still has the file open, deleting it removes the directory entry but the process keeps holding the file handle. The space never gets freed. The file becomes a ghost — invisible to du but still consuming disk space.
Truncating sets the file size to zero while keeping the file in place, so the running process continues writing to it without any disruption.
sudo truncate -s 0 /var/log/php8.3-fpm.log
One important thing — this common command does NOT work:
sudo echo "" > /var/log/php8.3-fpm.log # WRONG
The redirect > runs as your user, not root, so you get a permission denied error even with sudo. The correct alternative:
echo "" | sudo tee /var/log/php8.3-fpm.log
tee runs as root and handles the redirect correctly.
After truncating I run df -h again to confirm space is recovered:
/dev/sda1 40G 23G 17G 58% /
Crisis over. But this is only the immediate fix.
Step 5: Fix the Root Cause — Configure Logrotate
Clearing the file stops the bleeding. But if I don’t set up log rotation, this file will grow back to 15GB within days.
I check if logrotate is already configured for php-fpm:
ls /etc/logrotate.d/
Output:
apache2 apt dpkg mysql-server nginx rsyslog ufw
No php-fpm entry. That’s exactly why this happened — nobody configured log rotation for it so it grew unchecked.
I create /etc/logrotate.d/php8.3-fpm:
/var/log/php8.3-fpm.log {
daily
rotate 7
compress
missingok
notifempty
create 0640 www-data adm
}
What each directive does:
daily— rotate the log every dayrotate 7— keep 7 days of old logs, delete anything oldercompress— compress old logs with gzip to save spacemissingok— don’t error if the log file is missingnotifempty— skip rotation if the file is emptycreate 0640 www-data adm— create a fresh empty log file after rotation with correct permissions
Then I test the configuration:
sudo logrotate --debug /etc/logrotate.d/php8.3-fpm
--debug runs a dry run without actually rotating anything. It tells me if the config has any syntax errors before I rely on it in production.
The Full Chain
Looking back at this investigation, the chain of commands was:
Alert → df -h → du -h / → du -h /var → du -h /var/log → tail log → truncate → logrotate
Each step narrows the search. df gives the filesystem, du gives the directory, tail gives the content, truncate fixes the immediate problem, logrotate prevents recurrence.
This is the systematic approach I follow every time. No guessing, no random deletion — just a clean drill-down from the top level to the exact file causing the problem.
Running Ubuntu 24.04 on a Hetzner CPX22 VPS. Stack: nginx + php-fpm 8.3 + MySQL + WordPress.
Leave a Reply