背景
树莓派系统备份有两种常见方式:一种是直接使用镜像工具进行整卡复制,另一种是以目录形式保存完整 Linux 根文件系统。后者的优点是灵活,可以在不同容量的 SD 卡之间迁移,也方便排除临时文件、伪文件系统和缓存内容。
本次操作的目标是:使用 Raspberry Pi 官方写盘工具先写入一个新的可启动系统,让它自动建立 SD 卡分区结构;然后保留这个分区结构,删除新系统文件,再把已有的完整系统备份同步进去。最后修改新 SD 卡的 UUID,使系统能够正常启动。
这种方式适合以下场景:
- 已有完整的树莓派系统文件备份;
- 不想手动重新分区;
- 希望保留官方工具生成的启动分区和根分区结构;
- 需要把同一套系统恢复到一张或多张新的 SD 卡上。
基本思路
整个流程可以概括为:
使用 Raspberry Pi Imager 写入基础系统
→ 保留 SD 卡分区结构
→ 挂载根分区和启动分区
→ 删除 SD 卡现有系统文件
→ rsync 还原备份文件
→ 获取新 SD 卡 UUID
→ 修改 /etc/fstab
→ 修改 /boot/firmware/cmdline.txt
→ sync 并安全卸载
其中最关键的地方是:还原文件之后,不能继续使用旧备份里的 UUID。必须把 fstab 和 cmdline.txt 中的 UUID 改成当前 SD 卡实际分区的 UUID。
事前确认
假设:
DISK="/dev/sdX"
SRC="/path/to/raspberry-backup"
TARGET="/mnt/rpi-root"
其中:
/dev/sdX 新 SD 卡整块设备
/path/to/raspberry-backup 已有的完整系统备份目录
/mnt/rpi-root 临时挂载点
备份目录应当是完整的 Linux 根文件系统,顶层通常能看到:
bin
boot
etc
home
root
usr
var
如果备份使用的是较新的 Raspberry Pi OS 或 Ubuntu for Raspberry Pi,启动文件通常位于:
/boot/firmware/cmdline.txt
对应地,启动分区应挂载到:
/mnt/rpi-root/boot/firmware
第一步:用 Raspberry Pi Imager 写入基础系统
先使用 Raspberry Pi 官方写盘工具写入一个新的系统。这里的目的不是使用这个新系统本身,而是让 Imager 自动建立正确的分区结构和启动分区。
写入完成后,重新插入 SD 卡,然后确认设备名:
lsblk -o NAME,PATH,SIZE,MODEL,FSTYPE,LABEL,UUID,MOUNTPOINTS /dev/sdX
正常情况下,应该看到类似结构:
sdX
├─sdX1 vfat system-boot
└─sdX2 ext4 writable
其中:
sdX1 启动分区,FAT32
sdX2 根分区,ext4
如果系统自动挂载了分区,需要先卸载:
sudo umount /dev/sdX1 /dev/sdX2 2>/dev/null || true
第二步:确认根分区容量
有些官方镜像写入后,根分区可能只占用了镜像原始大小,没有自动扩展到整张卡。
可以检查:
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS /dev/sdX
如果 /dev/sdX2 明显小于 SD 卡剩余空间,需要先扩展分区:
sudo parted /dev/sdX --script resizepart 2 100%
sudo partprobe /dev/sdX
sudo e2fsck -f /dev/sdX2
sudo resize2fs /dev/sdX2
再次确认:
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS /dev/sdX
此时 /dev/sdX2 应当已经扩展到接近整张 SD 卡的剩余容量。
第三步:挂载根分区
创建挂载点并挂载根分区:
sudo mkdir -p /mnt/rpi-root
sudo mount /dev/sdX2 /mnt/rpi-root
确认挂载结果:
findmnt /mnt/rpi-root
第四步:删除 SD 卡根分区现有系统文件
这一步只删除 SD 卡里的新系统文件,不会影响备份目录。
sudo find /mnt/rpi-root -mindepth 1 -maxdepth 1 -exec rm -rf --one-file-system {} +
确认根目录已清空:
ls -la /mnt/rpi-root
第五步:挂载启动分区
由于该类树莓派系统使用 /boot/firmware 结构,因此需要先创建目录,再挂载启动分区:
sudo mkdir -p /mnt/rpi-root/boot/firmware
sudo mount /dev/sdX1 /mnt/rpi-root/boot/firmware
确认挂载:
findmnt /mnt/rpi-root/boot/firmware
第六步:删除启动分区现有文件
sudo find /mnt/rpi-root/boot/firmware -mindepth 1 -exec rm -rf {} +
确认启动分区已清空:
ls -la /mnt/rpi-root/boot/firmware
第七步:同步根文件系统
设置备份源目录:
SRC="/path/to/raspberry-backup"
使用 rsync 同步根文件系统,但排除启动分区内容和伪文件系统:
sudo rsync -aAXHv --numeric-ids \
--exclude='/boot/firmware/*' \
--exclude='/dev/*' \
--exclude='/proc/*' \
--exclude='/sys/*' \
--exclude='/tmp/*' \
--exclude='/run/*' \
--exclude='/mnt/*' \
--exclude='/media/*' \
--exclude='/lost+found' \
"$SRC"/ /mnt/rpi-root/
这里需要注意源路径末尾的 /:
"$SRC"/
这个斜杠表示复制备份目录里面的内容,而不是把整个备份目录本身复制进去。
第八步:单独同步启动分区
启动分区是 FAT32,不需要保留 Linux 权限、ACL、xattr 等属性,因此单独使用较简单的参数同步:
sudo rsync -rtv --delete \
"$SRC"/boot/firmware/ /mnt/rpi-root/boot/firmware/
第九步:获取新 SD 卡 UUID
还原后的系统文件里通常仍然保留旧系统的 UUID,因此必须获取当前 SD 卡的新 UUID:
BOOT_UUID=$(sudo blkid -s UUID -o value /dev/sdX1)
ROOT_UUID=$(sudo blkid -s UUID -o value /dev/sdX2)
echo "BOOT_UUID=$BOOT_UUID"
echo "ROOT_UUID=$ROOT_UUID"
其中:
BOOT_UUID 启动分区 UUID
ROOT_UUID 根分区 UUID
第十步:修改 /etc/fstab
直接重写目标系统中的 fstab:
sudo tee /mnt/rpi-root/etc/fstab >/dev/null <<EOF
UUID=$ROOT_UUID / ext4 defaults 0 1
UUID=$BOOT_UUID /boot/firmware vfat defaults 0 1
EOF
确认内容:
cat /mnt/rpi-root/etc/fstab
应当类似:
UUID=<ROOT_UUID> / ext4 defaults 0 1
UUID=<BOOT_UUID> /boot/firmware vfat defaults 0 1
第十一步:修改 cmdline.txt
树莓派启动时会读取 cmdline.txt 中的 root=UUID=... 参数。需要把它替换成当前根分区 UUID:
sudo sed -i -E "s#root=UUID=[^ ]+#root=UUID=$ROOT_UUID#" /mnt/rpi-root/boot/firmware/cmdline.txt
确认:
cat /mnt/rpi-root/boot/firmware/cmdline.txt
其中应该包含:
root=UUID=<ROOT_UUID>
注意:cmdline.txt 必须保持为一整行,不要手动拆成多行。
第十二步:同步并卸载
sync
sudo umount -R /mnt/rpi-root
这一步可能需要等待一段时间。大量小文件写入 SD 卡后,Linux 可能仍有写缓存需要刷入设备。sync 或 umount 等待几分钟并不一定异常。
如果需要观察是否仍在写入,可以另开终端查看:
watch -n 2 'grep -E "Dirty|Writeback" /proc/meminfo; echo; lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS /dev/sdX'
如果怀疑设备异常,可以查看内核日志:
dmesg -T | grep -Ei 'sdX|I/O error|buffer I/O|reset|fail|readonly|sense' | tail -80
安全卸载后,再确认挂载点为空:
lsblk -o NAME,PATH,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS /dev/sdX
如果 MOUNTPOINTS 为空,就可以拔出 SD 卡,插入树莓派启动。
关于 Raspberry Pi Imager 的异步写入提示
新版 Raspberry Pi Imager 在 Linux 下可能会尝试使用异步写入、direct I/O、zero-copy 等优化路径,以提高写入速度。如果终端中出现类似:
Async write queue failed, falling back to sync
通常表示高速写入路径失败,程序回退到普通同步写入方式继续执行。
这类提示不一定代表 SD 卡损坏。判断 SD 卡是否有问题,更应关注以下情况:
Write failed
Verify failed
I/O error
Buffer I/O error
device reset
read-only
如果 Imager 最终显示写入和验证成功,一般可以继续后续还原操作。
两张 SD 卡写入相同系统时的注意点
如果要把同一份系统备份还原到两张 SD 卡上,并用于两台完全相同的树莓派,需要注意:两台机器不能长期保持完全相同的系统身份。
同源系统可能包含相同的:
hostname
/etc/machine-id
SSH host key
DHCP 相关标识
如果两台设备同时接入同一个局域网,可能造成:
路由器中设备名称重复
SSH 主机指纹冲突
日志和监控难以区分
局域网 hostname 解析混乱
因此建议第一次启动时,一台一台开机并进行身份分离。
例如第一台:
sudo hostnamectl set-hostname rpi-main
sudo rm -f /etc/machine-id
sudo systemd-machine-id-setup
sudo rm -f /etc/ssh/ssh_host_*
sudo dpkg-reconfigure openssh-server
sudo systemctl restart ssh
sudo reboot
第二台:
sudo hostnamectl set-hostname rpi-test
sudo rm -f /etc/machine-id
sudo systemd-machine-id-setup
sudo rm -f /etc/ssh/ssh_host_*
sudo dpkg-reconfigure openssh-server
sudo systemctl restart ssh
sudo reboot
这样,两张 SD 卡虽然来自同一个备份,但两台树莓派在网络和系统层面会被识别为两台独立设备。
总结
这种还原方式的优势在于:
分区结构由 Raspberry Pi Imager 负责
系统内容由已有备份恢复
启动参数和挂载配置手动修正
核心风险点只有几个:
不要写错 SD 卡设备名
根分区容量要足够
rsync 源路径末尾要带 /
fstab 必须写入新 UUID
cmdline.txt 必须写入新 ROOT_UUID
卸载前必须等待 sync 完成
两台克隆系统首次启动后要分离身份
完成这些步骤后,SD 卡就不再是 Raspberry Pi Imager 写入的新系统,而是:
官方工具建立的可启动分区结构
+
已有系统备份的完整文件
+
当前 SD 卡实际 UUID
这是一种比整卡镜像更灵活、也比完全手动分区更稳妥的树莓派系统恢复方式。