How to recover deleted files on Linux
Linux has no undo for rm: it removes the file's name and marks its space free for reuse. The reliable way back is a backup or snapshot. Without one, check whether a program still has the file open, so you can copy it out of /proc; otherwise stop writing to that filesystem and run recovery tools on a copy of the disk, knowing they often fail on ext4 and find nothing once the disk has been trimmed.
The honest answer: restore it from a backup
GNU rm's manual says only that it might be possible to recover some of a removed file's contents, given sufficient expertise and time. On ext4 the odds are poor: ext4 maintainer Theodore Ts'o wrote in 2021 that ever since ext3, unlinking a file zeroes the inode's block pointers and extent tree, so the filesystem forgets where the data was. Since Linux 5.13, ext4 also wipes the directory entry, so the name goes too. Tools have to guess, from old copies in the journal or by recognizing file contents.
| Situation | What to do | Chances |
|---|---|---|
| A program still has the file open | Copy it from /proc | Good, while that program runs |
| A backup or snapshot from before the deletion | Restore just that file | Good |
| ext3 or ext4, deleted minutes ago, no backup | Image the disk; try extundelete, then PhotoRec | Possible, often partial |
| XFS or Btrfs, no backup | Image the disk; PhotoRec, or btrfs restore | Possible, often partial |
Disk mounted with discard, or fstrim has run since | Restore from a backup | Effectively none |
First, stop the damage
Any write to that filesystem can land on the freed blocks: logs, temp files, installing the recovery tool. In order:
- Check for open copies before stopping anything (next section). Stopping or restarting the program that holds a deleted file open frees it for good.
- Stop the services that write to that filesystem.
- If it is a separate filesystem such as
/data, remount it read-only; if the mount is busy, stop whatever still writes there and retry. - If it is the root filesystem, shut the server down and attach its disk to another server, or boot your provider's rescue system.
- Image it to a different disk and work on the image; extundelete and PhotoRec both accept an image file.
bs=4Mcopies 4 MB at a time instead of dd's 512-byte default, andstatus=progressshows progress.
sudo mount -o remount,ro /datasudo dd if=/dev/vdb1 of=/mnt/rescue/vdb1.img bs=4M status=progressThese two and the recovery tools below need a real disk, so they were checked against their manuals rather than run on our test server. Never recover files to the filesystem you are recovering from.
If a program still has it open, copy it from /proc
A deleted file's data stays on disk until the last program holding it open closes it: a log deleted while the web server writes to it, a SQLite database deleted under a running app. +L1 lists open files whose link count is below 1, meaning they have no name left:
sudo lsof +L1In our test, tail -f stood in for a daemon, holding open an orders.csv we then deleted:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
tail 1776325 root 3r REG 253,1 32 0 2658784 /tmp/…/orders.csv (deleted)The PID is 1776325 and 3r is file descriptor 3, open for reading. lsof -a -p 1776325 +L1 narrows the list to one process (-a ANDs the conditions), and +aL1 /data to one filesystem. Without lsof, ls -l /proc/1776325/fd shows the same link: 3 -> …/orders.csv (deleted). Reading that link reads the deleted file:
sudo cp /proc/1776325/fd/3 orders.csv.recoveredThe copy had all 32 bytes. You cannot give the original its name back: ln on the /proc link failed with Invalid cross-device link, and ln -L with No such file or directory.
- Copy before you stop or restart the program; after our test killed
tail, its/procentry was gone, and so was the file. - A file still being written, such as a log, is copied as it is at that moment; let a database check a copied database file.
- Reading another user's /proc links needs root or that user, under the kernel's ptrace access check.
Check whether TRIM has already erased it
SSDs and thin-provisioned virtual disks accept discard (TRIM) requests: the filesystem tells the disk which blocks are free, and the disk may unmap or erase them. util-linux's blkdiscard manual is blunt: all data in the discarded region is lost. With the discard mount option, ext4 sends discards as soon as blocks are freed; without it, fstrim trims all free space at once, weekly from Ubuntu's fstrim.timer. Check the mount options:
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS -T /homeTARGET SOURCE FSTYPE OPTIONS
/ /dev/vda1 ext4 rw,relatime,discard,errors=remount-ro,commit=30On the Ubuntu 24.04 cloud server we tested on, the root filesystem is mounted with discard, so a deleted file's blocks are discarded at once. The weekly trim:
systemctl list-timers fstrim.timerNEXT LEFT LAST PASSED UNIT ACTIVATES
Mon 2026-10-05 01:07:53 UTC 8h Mon 2026-09-28 00:12:11 UTC 6 days ago fstrim.timer fstrim.service
1 timers listed.
Pass --all to see loaded but inactive timers, too.And whether the disk accepts discards at all:
lsblk --discardNAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
vda 0 512B 2G 0
├─vda1 0 512B 2G 0
├─vda14 0 512B 2G 0
├─vda15 0 512B 2G 0
└─vda16 0 512B 2G 0
vdb 0 512B 2G 0Non-zero DISC-GRAN and DISC-MAX mean the disk accepts discards; the kernel documents a maximum of 0 as no support. Don't read anything into DISC-ZERO: the kernel now always reports 0 there and says not to rely on any particular behavior after a discard. If the filesystem has discard, or the timer has run since the deletion, go straight to your backups.
ext4: debugfs, extundelete and PhotoRec
Run these against the image or an unmounted filesystem, with their output on another disk.
debugfs comes with e2fsprogs and opens a filesystem, or an image file, read-only unless you pass -w. Its lsdel request lists deleted inodes, the classic ext2 undelete. On ext4 it rarely helps, because the block maps it needs are already zeroed; Ts'o judged that almost nobody had used the lsdel-and-link approach for years.
sudo debugfs -R lsdel /mnt/rescue/vdb1.imgextundelete searches the journal for an older copy of the deleted file's inode, which still says where the data was. It works on ext3, and on ext4 with a journal, unmounted or read-only (or an image), and writes what it finds to RECOVERED_FILES in the current directory, so change to another disk first. Its last release, 0.2.4, came out in January 2013; treat it as unmaintained, though Debian and Ubuntu still package it. The path is relative to the filesystem's root:
cd /mnt/rescuesudo extundelete vdb1.img --restore-file var/www/shop/uploads/orders.csv--restore-all tries everything, and --after takes a time in seconds since 1970 (date -d '2026-10-04 09:00' +%s). Its own instructions stress working quickly until the filesystem is unmounted, because background writes can overwrite what you are trying to save.
PhotoRec is an open-source (GPL) tool from CGSecurity, shipped in the testdisk package. It ignores the filesystem and finds files by recognizing their contents, so it works on any filesystem, but names, dates and folders are lost, and what it finds lands in numbered folders: recup_dir.1, recup_dir.2 and so on. It is interactive; choose the ext2/ext3/ext4 option when it asks for the filesystem type.
sudo photorec /log /d /mnt/rescue/recup_dir /mnt/rescue/vdb1.imgTestDisk, from the same package, finds lost partitions and repairs partition tables. Its undelete covers FAT, exFAT, NTFS and ext2, not ext3 or ext4, so on a Linux server it is the tool for a deleted partition, not a deleted file.
XFS and Btrfs
XFS has no undelete command: xfsprogs 6.6 on Ubuntu 24.04 ships repair, debugging and copy tools, none for deleted files. The third-party open-source xfs_undelete, not packaged by Debian or Ubuntu, scans for deleted inodes. Its README says file names and paths cannot be recovered, files in more than about 21 extents cannot be recovered, and most files come back padded with zeros. Otherwise, use PhotoRec on an image.
Btrfs has no undelete either. btrfs restore copies files out of a damaged filesystem into another directory without modifying it, and -D lists what it would copy. Its -t option reads an older root tree, whose address btrfs-find-root can find, and an older tree may still point to a file deleted shortly before. The docs present it as a salvage tool, not an undelete, so treat a result as luck:
sudo btrfs restore -D /dev/vdb1 /mnt/rescue/btrfs-outThe real Btrfs undo is a read-only snapshot taken before the mistake. The Btrfs docs add that a snapshot is not a backup: it shares its data blocks with the original, so damage to those blocks hits both.
Files deleted inside Docker volumes
A named Docker volume is a directory on the host, by default /var/lib/docker/volumes/<name>/_data, so everything above applies to the host's disk. Two differences:
- Containers' processes are host processes. A process is visible from every ancestor PID namespace, under a host PID. Run
lsof +L1on the host rather than inside the container, and copy from the host's/proc. - Whole volumes are easy to delete.
docker compose down -vremoves the named volumes declared in the compose file and anonymous volumes, anddocker volume prune --allremoves every volume no container uses (without--all, only anonymous ones). Either is anrm -rfof the data.
Back volumes up with a throwaway container, as in Docker volume backups.
Restore one file from a backup
Restore into a staging directory, check the file, then copy it into place. From a tar archive, find the file's exact name first:
tar -tzf www-2026-10-03.tar.gz | grep ordersvar/www/shop/uploads/orders.csvmkdir -p restoretar -xzf www-2026-10-03.tar.gz -C restore var/www/shop/uploads/orders.csvThat extracted only restore/var/www/shop/uploads/orders.csv. Use the name exactly as listed: GNU tar strips the leading / when it creates an archive, and asking for /var/www/shop/uploads/orders.csv failed with tar: /var/www/shop/uploads/orders.csv: Not found in archive (exit status 2). -O prints a member instead of writing it:
tar -xzf www-2026-10-03.tar.gz -O var/www/shop/uploads/orders.csv- tar, more: wildcards and ownership on extract are in the tar guide. restic:
restorewith--includeand a--targetdirectory brings back one path (restic guide). - Cloud snapshot: on AWS, make a volume from the snapshot, attach it and mount it read-only (EBS snapshots). DigitalOcean, Hetzner and Vultr snapshots restore as whole servers: create a temporary one, copy the file across, then delete it (DigitalOcean snapshots).
- LVM or ZFS snapshot: mount the LVM snapshot read-only, or copy from ZFS's
.zfs/snapshotdirectory (LVM, ZFS).
Make the next one easy to undo
- Backups kept long enough, because some deletions go unnoticed for weeks (retention), and restored now and then (restore drill).
- Frequent snapshots where they are cheap: LVM, ZFS, Btrfs read-only snapshots, or your provider's.
- A prompt on big deletions. With the alias below,
rmasks once before removing more than three files or anything recursively. In our test it askedrm: remove 4 arguments?andrm: remove 1 argument recursively?;\rmskips the alias. - A trash can for people. The
trash-clipackage in Debian and Ubuntu moves files to a trash directory with their original path and deletion date, and itstrash-restoreputs them back. Thesafe-rmpackage refuses to delete paths listed in/etc/safe-rm.conf.
alias rm='rm -I'Aliases only work in interactive shells: bash does not expand them in scripts or cron jobs. Scripts need their own guards, such as the prune script in the find guide, and backups remain the real answer.
Frequently asked questions
- Can I undo rm on Linux?
- No. rm removes the file's name and frees its space. Restore it from a backup or snapshot. If a program still has it open, copy it from
/proc/<pid>/fd. Otherwise, recovery tools on a copy of the disk are a best effort. - Where do deleted files go on Linux?
- Nowhere. rm doesn't use a trash folder; desktop file managers do. The trash-cli package gives servers the same kind of trash, for files you delete with it.
- Can deleted files be recovered from an SSD?
- Usually not once the space has been trimmed. If the filesystem is mounted with discard, that happens at deletion; otherwise fstrim does it, typically weekly. util-linux's manual says data in a discarded region is lost.
- Does extundelete still work on ext4?
- Sometimes, for recent deletions on a filesystem with a journal, because it looks for old copies of the file's inode there. Its last release was in January 2013. Run it on an image, never on the live disk.
- How do I recover a deleted file that is still open?
- Run
sudo lsof +L1to find the process ID and file descriptor, then copy/proc/<pid>/fd/<fd>to a new file before that process stops.
How this was checked
The commands were run on Ubuntu 24.04 LTS, lsof 4.95.0, GNU tar 1.35, util-linux 2.39.3 on October 4, 2026. Any that need something this test server does not have, such as a second server, a cloud account or another database engine, were checked against the official pages below instead.
Sources, on October 4, 2026:
- Ubuntu manpage: lsof (+L1, -a, -p)
- proc_pid_fd(5) manual
- pid_namespaces(7) manual
- rm(1) manual (GNU coreutils)
- Theodore Ts'o on LKML: what ext3 and ext4 zero on unlink (2021)
- Linux kernel commit: ext4: wipe ext4_dir_entry2 upon file deletion
- ext4 wiki (archived at kernel.org): Undeletion
- debugfs(8) manual (e2fsprogs)
- extundelete home page
- extundelete command-line options
- Ubuntu packages: extundelete in noble
- Ubuntu packages: testdisk in noble (includes PhotoRec)
- CGSecurity: PhotoRec
- CGSecurity: PhotoRec step by step
- CGSecurity: TestDisk
- Ubuntu manpage: photorec
- xfs_undelete README
- Btrfs documentation: btrfs restore
- Btrfs documentation: btrfs-find-root
- Btrfs documentation: Subvolumes and snapshots
- Linux kernel docs: ext4 mount options (discard)
- Linux kernel docs: sysfs-block (discard_max_hw_bytes, discard_zeroes_data)
- fstrim(8) manual
- blkdiscard(8) manual
- mount(8) manual (remount)
- dd(1) manual
- Docker Docs: Volumes
- Docker Docs: docker compose down
- Docker Docs: docker volume prune
- Ubuntu manpage: trash-put (trash-cli)
- Ubuntu manpage: safe-rm
- bash(1) manual (aliases)