How to Recover Files Deleted with rm -rf
You run rm -rf for wrong path? Stop writes first, then see which recovery options fit work on ext4 before new data overwrite your deleted files.
Wetin to do for di first sixty seconds
Two things dey decide whether you fit recover files wey you delete with rm -rf, and both of dem happen before you open search engine. Stop writing to that filesystem. Then remove am from use, either by unmounting am or remounting am read-only.
rm no dey erase anything. E remove the directory entry, then mark the inode and the file data blocks as free. The bytes still dey on the device. Dem go remain there until block allocator give those blocks to another thing and that thing write over dem. Every second wey filesystem remain mounted and busy, daemon fit write one log line or database fit flush one page. Any of those writes fit land for the blocks wey you want back.
So, the first commands na the ones wey stop writes, no be the ones wey recover files.
sudo systemctl stop nginx postgresql
sudo umount /mnt/dataIf umount answer umount: /mnt/data: target is busy., find wetin dey hold the filesystem open.
sudo fuser -vm /mnt/data
sudo lsof +D /mnt/dataIf you no fit free am, remount am read-only instead. Read-only mount stop new allocations, and na most of wetin you need be that.
sudo mount -o remount,ro /mnt/dataIf the deleted path dey on the root filesystem, this one harder. sudo mount -o remount,ro / go usually fail with mount: /: cannot remount /dev/vda1 read-only., because running processes dey hold files open for writing and kernel no go force dem close. For VPS, the practical answer na your provider rescue or recovery mode: e boot separate live system with your disk attached but not mounted. Every command below go then run against device wey nobody dey write to.
One rule apply to the whole of this guide. Never write recovered files, disk image, or newly installed tool onto the filesystem wey you dey recover from. Attach second volume, or send the output go another machine over SSH.
Why rm -rf recovery for ext4 nearly no hope
Before you install anything, set your expectation correctly. Confirm which filesystem you dey use:
lsblk -fFor ext4, wey be the default for almost every VPS image, file data location dey inside the inode as extent tree. Extent na one record wey dey show say logical block N for this file start for physical block M and cover L blocks. Small files fit keep up to four of these records inside the inode itself. Bigger files dey point to extra blocks wey hold the remaining part of the tree.
When the last link to file disappear, ext4 go walk through that tree, return every extent to block allocator, and clear the tree from the inode. Then e go mark the inode as free and write deletion time for am. The data itself no change. But the only record wey show where that data dey has been erased.
Na this be the difference from ext3, where deleted inode still keep enough information for tool like ext3grep to follow. You still fit list deleted inodes for ext4:
sudo debugfs -R lsdel /dev/vdb1debugfs dey open the device read-only unless you pass -w, so this one safe for unmounted device and e cost nothing to try. E go list inodes. The problem start when you try dump one, because the block map wey the inode use to hold don clear, so dump get nothing to follow.
Two tools dey try solve this by reading the journal. Journal na fixed-size ring wey ext4 dey use to keep metadata consistent after crash, and e fit still contain older copy of the inode from before deletion. extundelete and ext4magic both search the journal. Check the size wey you dey work with:
sudo dumpe2fs -h /dev/vdb1 | grep -i journalJournal dey hold metadata only and e small, so normal write activity dey cycle through am quickly. For running server, the time wey pre-delete inode still dey available na just some minutes. Nobody dey actively maintain either tool, and every distribution no package either one. Treat both as unlikely options. Run dem against unmounted device or disk image, and no surprise if dem return nothing.
If lsblk -f report xfs, the situation no better, because XFS no get supported undelete too. The order of options below no change.
File still dey open for a process wey dey run?
Na this recovery for this page get good chance to work. Na why you no suppose restart the service wey dey use the file.
File only truly disappear when two counts reach zero: the number of directory entries wey dey point to its inode, and the number of open file descriptors. rm dey reduce the first count to zero. If process still hold the file open, the second count no be zero. So the inode and its blocks still dey allocated, and you still fit read the data.
Find open files wey their link count don drop to zero:
sudo lsof +L1+L1 mean say make e list open files wey get link count below 1. Each result show the process, the file descriptor number, an NLINK of 0, and a path wey end with (deleted). Take the PID and descriptor number go /proc:
sudo ls -l /proc/1234/fdEntry fit look like 3 -> /var/log/app/events.log (deleted). That link still reach the data. Copy am go another filesystem:
sudo cp /proc/1234/fd/3 /mnt/rescue/events.logUse cp, no be mv. When you open /proc/1234/fd/3, e give you fresh handle for the same inode, starting from offset zero. This one give you the complete file, instead of only the part after where the writer current position dey.
Two limits dey wey you suppose know. Deleted directory tree no fit come back this way, because na only the individual files wey process had open still dey held. Also, if you copy database file while the engine dey write, na crash-consistent copy you go get. So plan to run the engine own recovery on am, instead of treating am as clean. Entries wey lsof show with mem instead of descriptor number na memory mapped. Dem no get /proc/<pid>/fd entry wey you fit copy from.
You get snapshot for btrfs, ZFS or LVM?
If the filesystem dey take snapshots, the deleted files don already dey inside one, unchanged. This only help if snapshot dey before the delete happen. Anything wey you create now no fit go back reach the past.
btrfs dey keep snapshots as subvolumes:
sudo btrfs subvolume list /Browse the snapshot and use cp -a copy out the paths wey you need. E better make you copy individual paths instead of rolling back the whole subvolume, because rollback go also discard everything wey dem write since dem take the snapshot.
ZFS dey show every snapshot as read-only directory:
zfs list -t snapshot
ls /tank/data/.zfs/snapshot/The .zfs directory dey hidden, and e no go show for plain ls of the dataset root, but you fit enter am by name. Copy files comot from there. zfs rollback go move the whole dataset back and destroy every snapshot wey newer pass the one wey you name, so keep am as last resort.
LVM snapshots na copy-on-write volumes wey get fixed size:
sudo lvs
sudo mount -o ro /dev/vg0/data-snap /mnt/snapMount am read-only and copy files comot. Check lvs before you trust am, because when LVM snapshot fill the space wey dem allocate for am, kernel go invalidate am. Once that happen, the contents don lost.
Snapshot no be backup. E dey live for the same disk or same pool as the original, so e share every failure wey fit affect the original. E good well well for undo mistake wey happen two minutes ago, and na exactly the work wey we need here.
Carving with PhotoRec, against an image and never the live disk
If nothing above applies, wetin remain na carving: scanning the raw device for byte patterns wey dey mark where known file type start, then writing out whatever data follow. Carving dey read file data only. Filenames, directory structure, timestamps and ownership na filesystem metadata, and na that metadata rm destroy, so none of dem go come back. You go get files wey dem call f0384512.jpg inside numbered output directory, and you go sort dem by hand.
Two rules dey decide whether this go work at all.
First, create image of the device before you point anything else at am. For Debian and Ubuntu, the package na gddrescue and the binary wey e install na ddrescue.
sudo apt update && sudo apt install -y gddrescue testdisk
sudo ddrescue -n /dev/vdb1 /mnt/rescue/vdb1.img /mnt/rescue/vdb1.map/mnt/rescue must dey for different device, and e must get free space wey at least equal the partition size. lsblk -b go print the exact sizes for bytes. The map file go make interrupted copy continue instead of starting from beginning again. Once you get the image, you fit later try another tool against exactly the same bytes. You no fit do this if the first tool don write over the disk.
Second, point the recovery tool to the image file.
sudo photorec /d /mnt/rescue/recup /mnt/rescue/vdb1.imgphotorec go open text menu. Choose the partition, then the filesystem type, then the file signatures to search for, then the destination directory. Reduce that signature list to the file types wey you actually lose before you start, because the default list dey find everything and go give you tens of thousands of fragments to check.
testdisk, from the same package, get its own undelete function, and e support FAT, exFAT, NTFS and ext2 only. For ext4, that means photorec.
Expect fragmented files to return broken. Carving assume say file blocks dey contiguous, so if allocator split file across the disk, the file fit reassemble wrongly or the tool fit miss am completely. Media files dey carve reasonably well because dem get strong headers. Plain text, configuration and source code dey carve badly, because no byte signature dey mark where shell script start.
Stray space: how wrong path delete happen
Almost every rm -rf accident na shell problem. rm dey receive list of paths and remove dem one after another. E no dey know wetin you mean.
The classic one na single space:
rm -rf /home/deploy/app /old
rm -rf /home/deploy/app/oldThe first line get two arguments. E delete the application first, then e delete /old. If /old no dey exist, rm no print anything, because -f dey suppress missing-file error. Silence no be confirmation.
The second pattern na unquoted variable wey hold space:
dir="/srv/my app"
rm -rf $dirShell dey split the value for whitespace, so rm dey receive /srv/my and app as two separate paths. When you write am as rm -rf "$dir", e be one path.
The third one na empty variable, usually because the command wey suppose fill am fail:
rm -rf "$TARGET"/*When TARGET no dey set, e expand to rm -rf /*. GNU rm no gree accept the bare form: rm -rf / print rm: it is dangerous to operate recursively on '/' and stop. The glob form no get this protection, because shell go replace /* with list of real top-level paths before rm ever run, and / no dey among dem, so the guard no go trigger.
Habits wey fit prevent the next one
- Quote every variable wey you use as a path. Write
"$dir"every time, including inside tests and loops. - Fail when e empty.
rm -rf "${TARGET:?TARGET is not set}"/*go make the shell stop with your message beforermstarts, wheneverTARGETno dey set or e empty. Putset -euo pipefailfor the top of any script wey dey delete. - Add
--one-file-system. E tellrmmake e skip any directory wey dey for another filesystem from the argument wey you give am, so recursive delete no fit enter mounted backup volume or bind mount. - No delete as root. Service account fit only destroy wetin e own, and na the main reason for running each service as its own unprivileged user. If you no sure wetin one account fit reach, reading the permission bits in an ls listing go answer am with one command.
- Print the list before you act on am. For script, build the paths,
printf '%s\n'dem, read the output, then delete for second pass. - Keep trash command close by.
sudo apt install trash-clidey give youtrash-put,trash-list,trash-restoreandtrash-empty. Deleted files go move to~/.local/share/Trash, andtrash-empty 30go clear anything wey old pass thirty days.
To alias rm to trash-put fit sound like the obvious next step, but na trap. The alias go train reflex wey go fail for the next server wey no get am, and aliases no dey apply inside scripts, na there expensive mistakes dey happen. Type trash-put deliberately instead.
Recovery wey go work every time
Everything wey dey above na chance. Backup no be chance.
Two things dey make backup real. E dey run for schedule without you needing remember am, and you don restore from am at least once. Repository wey nobody don ever restore from na belief, because the things wey fit make am useless, like wrong path for the include list or repository password wey nobody write down, go only show on the day you need am.
With restic, restore na two commands.
restic -r /srv/restic-repo snapshots
restic -r /srv/restic-repo restore latest --target /mnt/rescue --include /srv/appdataRestore go empty directory instead of over the live path, so you fit compare both before anything move enter the correct place. How to set up restic backups for VPS cover repository setup and the systemd timer wey dey run am.
With Borg:
borg list /srv/borg-repo
borg extract /srv/borg-repo::daily-2026-08-08 srv/appdataPaths inside Borg archive dey stored without the leading slash, so srv/appdata go match and /srv/appdata no go match anything. borg extract go write enter the current working directory, so cd go scratch directory first.
If you never choose between dem yet, comparison between restic and Borg cover deduplication and append-only repositories. Na this property dey stop breached server from deleting its own backup history. Any of the two tools dey okay. The wrong answer na make neither one dey run.
New server na the cheapest time to set this up, before anything wey worth losing dey on am. The first ten minutes for new VPS na where this work suppose happen, together with SSH and firewall setup.
Then put recurring entry for your calendar: every month, restore one directory from the repository into /tmp and read the files. That one habit worth pass every tool for this page.
FAQ
I fit recover file wey I delete for ext4?
Usually no. When last link to file commot, ext4 dey clear the extent tree from inode, so nothing for disk dey record where the data dey. extundelete and ext4magic dey search ext4 journal for older copy of that inode. This only help if delete happen some minutes ago and filesystem don quiet since then. Nobody dey actively maintain either project. Run either one against unmounted device or disk image, never against mounted filesystem. First use sudo dumpe2fs -h /dev/vdb1 | grep -i journal check wetin you dey work with.
Service still get the deleted file open. I fit bring am back?
Yes, and this na the best case. As long as process hold file open, the inode and data blocks remain allocated, so you still fit read the data. No restart the service, because closing the last descriptor go complete the delete. Run sudo lsof +L1 to list open files wey get link count of 0. Note the PID and file descriptor number, then copy through /proc with sudo cp /proc/1234/fd/3 /mnt/rescue/events.log. Write the copy to a different filesystem. Entries wey show mem instead of descriptor number dey memory mapped and no get /proc/<pid>/fd path wey you fit copy from.
Why I suppose image the disk instead of running recovery tool on am?
Because every tool must write its output somewhere, and if e write to the filesystem wey you dey recover, the write fit land on free blocks wey still hold your data. First copy the partition to different device with sudo ddrescue -n /dev/vdb1 /mnt/rescue/vdb1.img /mnt/rescue/vdb1.map. Then point photorec to the image file. The image also let you try another tool later against exactly the same bytes. This no possible after something don write over the original.
rm -rf / still fit destroy Linux system?
The bare command no fit. GNU rm refuse am and print rm: it is dangerous to operate recursively on '/'. The dangerous forms na the ones wey come through another route. rm -rf "$TARGET"/* with TARGET unset go expand to rm -rf /*, and shell go hand rm a list of real top-level directories. None of dem na /, so the guard no go trigger. Write "${TARGET:?TARGET is not set}" instead, and shell go stop before rm runs.
Filesystem snapshot na backup?
No. btrfs or ZFS snapshot dey for the same pool as the data wey e protect, so failed disk or destroyed pool go carry both at once. LVM snapshot get extra problem of fixed size: once e full, kernel go invalidate am and the contents go disappear. Snapshots dey very useful to undo delete wey happen two minutes ago. For everything else, keep repository for separate hardware.