You delete a 20 GB log file, run df -h, and expect instant relief.
But the filesystem is still 98% full.
You run du -sh /var/log, and it says the directory is small. You run df -h /var, and it still says the disk is almost full. At this point, it feels like Linux is lying.
It is not.
If you are dealing with a linux deleted file still using disk space lsof situation, the most likely cause is an open deleted file. The file name is gone from the directory, but a running process still has the file open. Until that process closes the file descriptor, Linux cannot reclaim the disk blocks.
This is one of those Linux issues that looks mysterious the first time and obvious forever after.
In this guide, you will learn why disk space is not freed after deleting files, how to find open deleted files with lsof, how to reclaim the space safely, and how to prevent the problem from coming back.
The Symptom: df Says Full, du Says Small
A classic example looks like this:
df -h /var
Output:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 50G 48G 2.0G 96% /var
Then you check directory usage:
sudo du -sh /var/*
Output:
1.2G /var/cache
3.1G /var/lib
900M /var/log
200M /var/tmp
The numbers do not add up.
df reports filesystem-level usage. It asks the filesystem how many blocks are allocated and available.
du walks the directory tree and sums visible files and directories.
So when df shows much more usage than du, ask this question:
Is disk space allocated to something that no longer has a visible filename?
Open deleted files are one of the most common answers.
Why Deleted Files Can Still Use Disk Space
On Linux and Unix-like systems, deleting a file usually means unlinking a name from a directory.
That distinction matters.
A file has data blocks on disk. It may also have one or more directory entries pointing to it. When you run:
rm /var/log/app.log
Linux removes the directory entry named app.log.
But if a process already opened that file, the process still has a file descriptor pointing to the underlying file object.
The file is not fully removed until both of these are true:
- No directory entry links to it.
- No process has it open.
A simplified timeline looks like this:
Application opens /var/log/app.log
↓
You delete /var/log/app.log
↓
Filename disappears from directory
↓
Application keeps writing to open file descriptor
↓
Disk space remains allocated
↓
Application closes file or exits
↓
Filesystem finally frees the space
That is why deleting a log file used by a running service often does not free space.
The name is gone. The data is not.
The Linux unlink(2) documentation explains this behavior directly: if a file is unlinked while a process still has it open, the file remains until the last file descriptor is closed.
Common Real-World Causes
This problem often appears during emergency cleanup.
Someone sees /var full and removes the biggest log file:
sudo rm /var/log/myapp/application.log
But the application is still running and still writing to the deleted file.
Common culprits include:
- Application log files
- Nginx or Apache access logs
- Java application logs
- Database logs
- Container runtime logs
- Deleted temporary files
- Rotated logs still held by old worker processes
- Long-running backup or export files
- Large files opened by Python, Java, Node.js, or Go applications
- Deleted files inside container overlay filesystems
This is especially common when log rotation is misconfigured. If a log file is renamed or removed but the process is not told to reopen its logs, it may continue writing to the old file descriptor.
How to Find Open Deleted Files with lsof
The fastest command is:
sudo lsof +L1
The +L1 option tells lsof to show files with a link count less than 1. In practice, this finds files that have been unlinked but are still open.
You may also see people use:
sudo lsof | grep deleted
That can work, but lsof +L1 is cleaner and more direct.
Example output:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
java 1842 appuser 12w REG 253,0 21474836480 0 9172 /var/log/app.log (deleted)
nginx 2201 root 5w REG 253,0 5368709120 0 8241 /var/log/nginx/access.log (deleted)
The important columns are:
| Column | Meaning |
|---|---|
COMMAND | Process name holding the deleted file |
PID | Process ID |
USER | User running the process |
FD | File descriptor number |
SIZE/OFF | File size or offset |
NLINK | Link count; 0 means no directory entry |
NAME | Original path, usually marked (deleted) |
In this example, Java is holding a deleted 20 GB log file. Nginx is holding a deleted 5 GB access log.
That space will not be reclaimed until those file descriptors are closed.
Filter Open Deleted Files by Filesystem
On a busy server, sudo lsof +L1 may produce many results.
If /var is the full filesystem, filter the output:
sudo lsof +L1 | grep '/var'
You can also check device numbers.
First, find the device for the filesystem:
df -h /var
Example:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 50G 48G 2.0G 96% /var
Then compare with the DEVICE column in lsof.
For a specific process:
sudo lsof -p 1842
For only deleted files from that process:
sudo lsof -p 1842 | grep deleted
To sort deleted files by size, use:
sudo lsof -nP +L1 |
awk 'NR==1 || $7 ~ /^[0-9]+$/ {print}' |
sort -k7 -n
For very large files, a more readable version is:
sudo lsof -nP +L1 |
awk 'NR==1 || $7 > 1073741824'
That shows deleted open files larger than 1 GB.
Why lsof Sometimes Needs sudo
Without elevated privileges, lsof may not show files opened by other users or system services.
This command:
lsof +L1
may return nothing.
But this command:
sudo lsof +L1
may reveal the real issue.
That difference is not cosmetic. Linux restricts process and file-descriptor visibility for security reasons. Always use sudo when investigating a production disk-space incident, unless your environment intentionally prevents it.
How to Reclaim Disk Space Safely
Once you identify the process, you have several options.
The safest choice depends on what the process is and how critical it is.
Option 1: Restart the Service
For most services, the cleanest fix is a controlled restart.
Example:
sudo systemctl restart myapp.service
Then verify:
df -h /var
sudo lsof +L1
If the process exits and restarts cleanly, the old file descriptor closes and disk space is released.
This is usually best for:
- Web servers
- Application services
- Background workers
- Log-writing daemons
- Services managed by systemd
Before restarting, check whether the service can tolerate it:
systemctl status myapp.service
And inspect dependencies, load balancers, replicas, or failover behavior.
Option 2: Reload the Service So It Reopens Logs
Some services support log reopening without a full restart.
For Nginx, you can often run:
sudo nginx -s reopen
Or:
sudo systemctl kill -s USR1 nginx.service
For many daemons, SIGHUP tells the process to reload configuration or reopen log files:
sudo systemctl reload myapp.service
But this depends on the application. Do not assume every process handles SIGHUP safely.
Check the application documentation first.
Option 3: Stop the Process
If the process is not critical:
sudo kill <PID>
If it does not exit:
sudo kill -TERM <PID>
Use kill -9 only as a last resort:
sudo kill -9 <PID>
SIGKILL prevents the process from cleaning up gracefully. It may leave temporary state, partial writes, or application-level corruption depending on what the process was doing.
Option 4: Truncate the Open File Descriptor
Sometimes you cannot restart the service immediately, but you must reclaim space.
On Linux, you can truncate the open file through /proc:
sudo truncate -s 0 /proc/<PID>/fd/<FD>
Example from lsof:
java 1842 appuser 12w REG 253,0 21474836480 0 9172 /var/log/app.log (deleted)
The PID is 1842, and the file descriptor is 12.
Truncate it:
sudo truncate -s 0 /proc/1842/fd/12
Then check:
df -h /var
sudo lsof -p 1842 | grep deleted
This can reclaim space without restarting the process.
However, use it carefully.
If the application continues writing to that descriptor, behavior depends on how it opened the file. If it writes at an old offset, the file may appear large again, sometimes as a sparse file. For logs, this is often acceptable during an emergency. For database files, active data files, or application state files, do not use this casually.
A good rule:
Truncating
/proc/<PID>/fd/<FD>is an emergency technique, not a normal maintenance workflow.
Never Delete Active Log Files as a Cleanup Strategy
Deleting a currently written log file is the mistake that creates this problem.
This is risky:
sudo rm /var/log/myapp/app.log
A safer emergency option is truncation:
sudo truncate -s 0 /var/log/myapp/app.log
Or:
sudo sh -c ': > /var/log/myapp/app.log'
Truncation keeps the same filename and inode. The running process can continue writing to the same file, and the disk blocks are released.
This works only if the file still exists at that path. If you already deleted it, use lsof +L1 and handle the open file descriptor.
For routine cleanup, use log rotation, not manual deletion.
df vs du: Why the Numbers Differ
Understanding df and du helps you diagnose the issue faster.
| Command | What it measures | Why it may differ |
df | Filesystem block allocation | Includes space used by open deleted files |
du | Visible files under a path | Cannot count files with no directory entry |
lsof +L1 | Open files with no remaining links | Finds deleted files still held by processes |
Example:
df -h /var
asks the filesystem:
How much space is allocated and free on this mounted filesystem?
This command:
sudo du -sh /var
walks visible directory entries under /var.
If a file has no directory entry, du cannot find it. But if the blocks are still allocated, df still counts them.
That is why the combination of df, du, and lsof is so powerful:
df -h /var
sudo du -xsh /var
sudo lsof +L1
The -x option tells du to stay on one filesystem, which helps avoid accidentally counting mounted filesystems under the directory.
Using /proc to Inspect Open Deleted Files
Linux exposes process file descriptors under:
/proc/<PID>/fd/
List them:
sudo ls -l /proc/1842/fd
You may see:
12 -> /var/log/app.log (deleted)
That means file descriptor 12 still points to the deleted file.
You can inspect file-descriptor metadata:
sudo cat /proc/1842/fdinfo/12
Example:
pos: 21474836480
flags: 0102001
mnt_id: 34
ino: 9172
This is useful when you want to understand whether the process is still writing and how large the offset has grown.
The proc_pid_fd(5) documentation explains that /proc/<PID>/fd/ contains symbolic links for open file descriptors.
What If lsof +L1 Shows Nothing?
Open deleted files are common, but they are not the only reason df and du disagree.
If lsof +L1 shows nothing, consider these possibilities.
1. You checked the wrong mount point
Run:
df -hT
Then check the exact filesystem:
df -h /path/that/is/full
sudo du -xsh /path/that/is/full
2. Files are hidden under a mount point
Suppose files were written to /mnt/data, and then another filesystem was mounted on /mnt/data. The old files become hidden beneath the mount.
They still consume space on the underlying filesystem, but du /mnt/data now sees the mounted filesystem, not the hidden files.
Check mounted filesystems:
findmnt
This issue often appears after failed mounts, backup jobs, or application startup ordering problems.
3. Reserved blocks
Some filesystems reserve space for root or system use.
On ext filesystems, inspect with:
sudo tune2fs -l /dev/sdXN | grep -i reserved
This affects available space, not usually a huge du vs df mystery, but it can explain why non-root users see less available space.
4. Snapshots or copy-on-write storage
On filesystems or storage layers with snapshots, deleting a file from the live filesystem may not free physical space if a snapshot still references the old blocks.
Check your storage layer:
- LVM snapshots
- Btrfs snapshots
- ZFS snapshots
- Thin-provisioned volumes
- Cloud disk snapshots
- Container overlay storage
5. Inode exhaustion
Sometimes the issue is not bytes but inodes.
Check:
df -ih
If inode usage is 100%, the filesystem may reject new files even when byte usage looks acceptable.
Open Deleted Files in Containers
Containers add another layer of confusion.
A process inside a container can hold a deleted file open on the host’s container storage, overlay filesystem, or mounted volume.
From inside the container, you might see only part of the picture. On the host, run:
sudo lsof +L1
If you use Docker, inspect container logs:
docker ps
docker logs <container>
Check Docker disk usage:
docker system df
For Kubernetes, inspect node disk pressure and container logs:
kubectl describe node <node-name>
kubectl logs <pod-name>
If a containerized process holds a deleted file open, restarting the Pod or container usually releases it:
kubectl delete pod <pod-name>
For a Deployment, prefer a controlled rollout:
kubectl rollout restart deployment/<deployment-name>
Be careful with node-level cleanup commands. Deleting container runtime files manually can corrupt runtime state.
Safer Log Rotation to Prevent the Problem
A correct log rotation setup prevents most open-deleted-file incidents.
With logrotate, one approach is to rotate logs and signal the service to reopen them.
Example:
/var/log/myapp/*.log {
daily
rotate 7
compress
missingok
notifempty
create 0640 appuser appuser
sharedscripts
postrotate
systemctl reload myapp.service >/dev/null 2>&1 || true
endscript
}
The key part is:
postrotate
systemctl reload myapp.service
endscript
That gives the application a chance to close the old file and open the new one.
Another option is:
copytruncate
copytruncate copies the log to a rotated file, then truncates the original file in place. This avoids requiring the application to reopen logs.
But copytruncate has tradeoffs:
| Method | Advantage | Risk |
| Signal/reload service | Clean handoff if app supports it | Requires correct reload behavior |
copytruncate | Works with apps that cannot reopen logs | Small chance of losing log lines during copy/truncate window |
Manual rm | Fast but unsafe | Can create open deleted files |
Manual truncate | Good emergency cleanup | Does not create rotated archive |
For production services, prefer application-supported log reopening. Use copytruncate only when the application cannot reopen logs cleanly.
A Practical Troubleshooting Workflow
When disk space is not freed after deleting files, use this sequence.
Step 1: Confirm the full filesystem
df -hT
Identify the mount point that is full.
Step 2: Compare visible usage
sudo du -xsh /var
Replace /var with the affected mount point.
Step 3: Search for open deleted files
sudo lsof -nP +L1
Step 4: Focus on the affected filesystem
sudo lsof -nP +L1 | grep '/var'
Step 5: Identify the process and file descriptor
Look for:
COMMAND PID USER FD SIZE/OFF NAME
Step 6: Choose the safest recovery action
Use this decision table:
| Situation | Recommended action |
| Non-critical process | Stop or restart it |
| systemd service | systemctl restart or systemctl reload |
| Web server log | Use reopen signal if supported |
| Critical process, log file only | Consider truncating /proc/<PID>/fd/<FD> |
| Database or data file | Do not truncate blindly; follow vendor recovery guidance |
| Container workload | Restart container or roll out Pod safely |
Step 7: Verify space is released
df -h /var
sudo lsof +L1
If df still does not change, continue investigating mount points, snapshots, reserved blocks, and inode usage.
Example Incident: Deleted Java Log Still Using 30 GB
Imagine /var is full.
df -h /var
shows:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 80G 79G 1.0G 99% /var
But:
sudo du -xsh /var
shows only:
42G /var
Now run:
sudo lsof -nP +L1 | sort -k7 -n
You find:
java 3921 appuser 14w REG 253,2 32212254720 0 881234 /var/log/payment/app.log (deleted)
The Java process is holding a deleted 30 GB log file.
If this service can restart:
sudo systemctl restart payment-api.service
Then:
df -h /var
now shows:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 80G 49G 31G 62% /var
The missing space was not hidden. It was still allocated to a file with no name but with an active file descriptor.
Conclusion
When Linux disk space is not freed after deleting files, do not assume the filesystem is broken.
If df says the disk is full but du cannot find the usage, a process may still be holding a deleted file open.
The key command is:
sudo lsof +L1
Once you find the process and file descriptor, choose the safest fix:
- Reload the service if it can reopen logs.
- Restart the service if a short interruption is acceptable.
- Stop the process if it is no longer needed.
- Truncate the open file descriptor only as a careful emergency measure.
Most importantly, avoid deleting active log files. Use log rotation, service reloads, and truncation when appropriate.
This problem is easy to miss because the file name disappears. But the kernel still knows about the open file, and lsof gives you the map.
Have you ever deleted a huge log file only to find that df did not change? Share the process name and the command that revealed it in the comments.
For more practical Linux, systemd, Docker, Kubernetes, and DevOps troubleshooting guides, subscribe to Codefy and explore the related tutorials.



