linux deleted file still using disk space lsof

Linux Disk Space Not Freed After Deleting Files? Find Open Deleted Files with lsof

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:

  1. No directory entry links to it.
  2. 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:

ColumnMeaning
COMMANDProcess name holding the deleted file
PIDProcess ID
USERUser running the process
FDFile descriptor number
SIZE/OFFFile size or offset
NLINKLink count; 0 means no directory entry
NAMEOriginal 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.

CommandWhat it measuresWhy it may differ
dfFilesystem block allocationIncludes space used by open deleted files
duVisible files under a pathCannot count files with no directory entry
lsof +L1Open files with no remaining linksFinds 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/&lt;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 &lt;container>

Check Docker disk usage:

docker system df

For Kubernetes, inspect node disk pressure and container logs:

kubectl describe node &lt;node-name>
kubectl logs &lt;pod-name>

If a containerized process holds a deleted file open, restarting the Pod or container usually releases it:

kubectl delete pod &lt;pod-name>

For a Deployment, prefer a controlled rollout:

kubectl rollout restart deployment/&lt;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>&amp;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:

MethodAdvantageRisk
Signal/reload serviceClean handoff if app supports itRequires correct reload behavior
copytruncateWorks with apps that cannot reopen logsSmall chance of losing log lines during copy/truncate window
Manual rmFast but unsafeCan create open deleted files
Manual truncateGood emergency cleanupDoes 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:

SituationRecommended action
Non-critical processStop or restart it
systemd servicesystemctl restart or systemctl reload
Web server logUse reopen signal if supported
Critical process, log file onlyConsider truncating /proc/<PID>/fd/<FD>
Database or data fileDo not truncate blindly; follow vendor recovery guidance
Container workloadRestart 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.