July 2, 2026
Backing Up a Live Raspberry Pi SD Card
I've got a Raspberry Pi that runs around the clock, and its entire world lives on one SD card. SD cards don't fail
gracefully — it's a question of when, not if. So I wanted a hands-off nightly backup. The catch: the Pi needs to stay up,
so I can't unmount the card to copy it cleanly. backup.sh clones the live card, block-for-block, onto a
second SD card with dd — no unmount required.
The script and full README live on GitHub.
How it works
A sync flushes filesystem buffers first, then dd copies the source device to the destination
device with conv=noerror,sync — noerror so a flaky read doesn't abort the whole run, and
sync so bad blocks get padded and every partition offset stays aligned.
Because the source is never unmounted, the result is a crash-consistent snapshot — the equivalent of yanking
the power — not a clean one. In practice that means the backup card may want an fsck on first boot, which the
Pi's kernel runs automatically. Good enough to get back on my feet if the primary card dies.
The scary part: pointing dd at the right disk
dd is a loaded gun. It overwrites the destination unconditionally, and kernel names like
/dev/sdb are not stable — a re-plug or a reboot can quietly reassign them. Point it at the
wrong disk and that disk is gone. Same lesson I wrote up for
my encrypted drive, where I learned it the hard way — I
unplugged an SD-card reader, rebooted, and the boot-time unlock came up with nothing to grab because sda had
quietly become sdb. Never trust /dev/sdX.
So the script pins each card by its filesystem UUID and resolves that UUID to the correct whole-disk
device at run time — the UUID always wins over any device name. The real UUIDs live in a gitignored .env on
the Pi, never in the repo:
SOURCE_UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
DEST_UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Guard rails
Since one wrong device means data loss, the script refuses to write a single byte if any of these fail:
- Not run as root.
- Source or destination is missing, or isn't a block device.
- The destination is smaller than the source.
- Any partition on the destination is currently mounted (that would mean it's a live filesystem, not a spare card).
- A pinned UUID can't be resolved, or source and destination resolve to the same disk.
Paranoid? Sure. But dd doesn't do second chances.
And there's a dry run that prints the exact dd it would execute and writes nothing:
sudo bash backup.sh --dry-run # prints the dd, writes nothing
sudo bash backup.sh # the real thing
Scheduling
It runs from root's crontab, nightly at 3 AM when the box is quiet:
0 3 * * * /path/to/backup.sh >> /var/log/pi-sd-backup.log 2>&1
Not fancy, but it means a dead SD card costs me a card swap and a reboot instead of a weekend of reinstalling and reconfiguring from memory. This is living documentation, so it'll grow as the setup does.