Why File-Level Recovery?
Full disk restores take hours and need significant storage. Often you only need a single config file or directory. File-level recovery lets you mount the image and extract exactly what you need in minutes.
Raw Disk Images
# Check partitions
fdisk -l /backup/server-disk.img
# Mount partition (calculate offset: start_sector x 512)
sudo mount -o loop,offset=537919488,ro /backup/server-disk.img /mnt/recovery
cp /mnt/recovery/etc/nginx/nginx.conf /tmp/
sudo umount /mnt/recovery
# Multiple partitions with losetup
sudo losetup -fP /backup/server-disk.img
sudo mount -o ro /dev/loop0p2 /mnt/recovery
cp -r /mnt/recovery/var/www/html /tmp/recovered-website
sudo umount /mnt/recovery
sudo losetup -d /dev/loop0
QCOW2 Images (KVM/Proxmox)
sudo apt install -y qemu-utils libguestfs-tools
# qemu-nbd method (fast, no conversion)
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 --read-only /backup/vm-100-disk-0.qcow2
sudo mount -o ro /dev/nbd0p1 /mnt/recovery
cp /mnt/recovery/etc/myapp/config.yml /tmp/
sudo umount /mnt/recovery
sudo qemu-nbd --disconnect /dev/nbd0
# libguestfs method (safer)
virt-filesystems -a vm-100-disk-0.qcow2 --long --all
virt-copy-out -a vm-100-disk-0.qcow2 /etc/nginx/nginx.conf /tmp/
virt-cat -a vm-100-disk-0.qcow2 /etc/hostname
VMDK Images (VMware)
sudo qemu-nbd --connect=/dev/nbd0 --read-only -f vmdk server.vmdk
sudo mount -o ro /dev/nbd0p1 /mnt/recovery
LVM Inside Disk Images
sudo qemu-nbd --connect=/dev/nbd0 --read-only /backup/vm-disk.qcow2
sudo vgscan && sudo vgchange -ay
sudo lvs
sudo mount -o ro /dev/vg_name/lv_root /mnt/recovery
cp /mnt/recovery/important-file.txt /tmp/
sudo umount /mnt/recovery
sudo vgchange -an vg_name
sudo qemu-nbd --disconnect /dev/nbd0
Automated Recovery Script
#!/bin/bash
# Usage: recover-file.sh image source_path dest_path
set -euo pipefail
IMAGE="$1"; SOURCE="$2"; DEST="$3"
MNT="/mnt/recovery-$$"
mkdir -p "$MNT"
FORMAT=$(qemu-img info "$IMAGE" | grep "file format" | awk '{print $3}')
cleanup() {
umount "$MNT" 2>/dev/null || true
qemu-nbd --disconnect /dev/nbd0 2>/dev/null || true
rmdir "$MNT" 2>/dev/null || true
}
trap cleanup EXIT
if [ "$FORMAT" = "raw" ]; then
OFFSET=$(fdisk -l "$IMAGE" | grep "Linux filesystem" | head -1 | awk '{print $2 * 512}')
mount -o loop,offset=$OFFSET,ro "$IMAGE" "$MNT"
else
modprobe nbd max_part=8 2>/dev/null || true
qemu-nbd --connect=/dev/nbd0 --read-only -f "$FORMAT" "$IMAGE"
sleep 1
PART=$(fdisk -l /dev/nbd0 | grep "Linux" | head -1 | awk '{print $1}')
mount -o ro "$PART" "$MNT"
fi
if [ -e "$MNT/$SOURCE" ]; then
cp -a "$MNT/$SOURCE" "$DEST"
echo "Recovered: $SOURCE"
else
echo "ERROR: $SOURCE not found"
exit 1
fi
Best Practices
- Always mount read-only (
-o ro) to prevent accidental modification - Use libguestfs/guestmount for safer access and broader format support
- Disconnect NBD devices and deactivate LVM after recovery
- Keep partition layout notes for faster emergency recovery
- For regular file-level recovery, use tools with native browsing (Restic mount, Borg mount)
- Test procedures before emergencies