Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 8 min read Last reviewed Jul 2026

Linux Error Guide: 'rmdir: failed to remove: Directory not empty' — Fix It Safely

Quick answer

Fix 'rmdir: failed to remove: Directory not empty' on Linux: reveal hidden and open files, spot busy mountpoints and NFS silly-rename leftovers, safely.

  • #linux
  • #troubleshooting
  • #errors
  • #filesystem
Free toolkit

Stuck on this Linux Admins error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

rmdir only removes empty directories, so it fails the moment anything remains inside — even things ls doesn’t show by default:

rmdir: failed to remove 'build': Directory not empty

You’ll see the same ENOTEMPTY condition from other tools and from rm when a mountpoint or busy file is involved:

rm: cannot remove 'cache': Directory not empty
mv: cannot move 'olddir' to 'newdir/olddir': Directory not empty

The frustrating case is when ls shows nothing but removal still fails. That almost always means hidden dotfiles, a filesystem mounted at that path, or deleted-but-still-open files the filesystem is holding onto (classically NFS “silly-rename” .nfs* files).

Symptoms

  • rmdir or rm -r reports “Directory not empty” on a directory that looks empty.
  • ls dir shows nothing but ls -A dir reveals hidden entries.
  • The directory is a mountpoint and refuses to empty until unmounted.
  • Files reappear immediately after you delete them (a process is recreating them).
  • On NFS, stubborn .nfsXXXX files block the removal.

Common Root Causes

  • Hidden dotfiles. .git, .env, .DS_Store, or .cache — invisible to plain ls, so the directory only looks empty.
  • It’s a mountpoint. A filesystem is mounted there; the mounted content can’t be deleted, only unmounted.
  • NFS silly-rename files. Deleting a file that another process still has open makes NFS rename it to .nfsXXXX; it stays until the handle closes.
  • A process is recreating files. A running service writes into the directory faster than you delete, so it’s never actually empty.
  • Nested subdirectories. rmdir is non-recursive; any subdirectory blocks it.
  • Immutable or special files. Files with the immutable attribute resist deletion, leaving the directory non-empty.

Diagnostic Workflow

First, see everything — including dotfiles — that’s actually in the directory:

ls -A dir            # -A shows hidden entries (except . and ..)
find dir -mindepth 1 -maxdepth 1 -printf '%y %p\n'   # type + name of each child

Check whether the path is a mountpoint rather than a normal directory:

mountpoint dir
findmnt -T dir

If files reappear or .nfs* files persist, find the process holding them open:

lsof +D dir 2>/dev/null | head
ls -A dir | grep -E '^\.nfs'

Look for immutable attributes that quietly block deletion:

lsattr dir 2>/dev/null
find dir -type f -exec lsattr {} \; 2>/dev/null | grep -- '-i-'

Example Root Cause Analysis

A cleanup script kept failing on a deploy host with rmdir: failed to remove 'releases/old': Directory not empty, yet ls releases/old printed nothing.

ls -A releases/old immediately revealed a .gitkeep and a hidden .env.local — the directory had never been empty, only visually empty. But after removing those, a second run still failed. This time lsof +D releases/old showed a long-running worker process with a log file open under that path; every time the script deleted the log, the worker had already reopened a new one, and on the NFS-mounted volume the delete had turned into a .nfs0000000123 silly-rename file that ls -A now exposed.

The correct fix was to stop the worker (or point it elsewhere) before cleanup, not to force-remove files it still held open. Once the process was stopped, the .nfs* handle closed, the directory truly emptied, and rm -r succeeded. The script was updated to check lsof +D and skip directories with open handles rather than fighting them.

Prevention Best Practices

  • Use ls -A (or find) when auditing “empty” directories so hidden files never surprise you.
  • Stop or drain processes that write into a directory before deleting it; never fight a service that’s actively recreating files.
  • Unmount mountpoints (and confirm with mountpoint) before trying to remove them; deleting through a mount is wrong anyway.
  • On NFS, close file handles before deletion to avoid silly-rename .nfs* leftovers, and expect them when apps hold deleted files open.
  • Prefer rm -rf only with a fully-qualified, double-checked path in scripts; log the target first so a variable that expands empty can’t wipe the wrong tree.

Quick Command Reference

# Reveal everything actually present
ls -A dir
find dir -mindepth 1

# Is it a mountpoint? Then unmount, don't delete
mountpoint dir
sudo umount dir

# Who is holding files open in there?
lsof +D dir

# Clear an immutable flag that blocks deletion
sudo chattr -i dir/stubbornfile

# Remove non-empty directory recursively (use a checked, absolute path)
rm -rf /full/path/to/dir

Conclusion

“Directory not empty” from rmdir simply means something is still inside — most often hidden dotfiles, a mounted filesystem, or deleted files a process still holds open (NFS .nfs* silly-renames). Use ls -A and find to see the real contents, mountpoint/findmnt to catch mounts, and lsof +D to catch open handles. Fix the underlying cause — stop the process, unmount the filesystem, clear an immutable flag — rather than blindly forcing removal, and always run rm -rf against a verified absolute path.

Free download · 368-page PDF

Fixed it? Get 500 Linux Admins & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.