Skip to content

Commit a5fead5

Browse files
committed
Updated Cloning notes
1 parent e18ebd7 commit a5fead5

File tree

3 files changed

+104
-33
lines changed

3 files changed

+104
-33
lines changed

BaseInstall.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ Basic Setup
310310

311311
9. **Miscellaneous**
312312

313+
- To prevent waiting for network on boot: `sudo systemctl mask NetworkManager-wait-online.service`
314+
313315
- To add a Wi-Fi network later, either use `sudo nmtui`, or run `nmcli --ask device wifi connect <SSID>`
314316
(may need to first disconnect from Wi-Fi for the latter). Hint: `nmcli device wifi list`
315317

Cloning.md

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ NUL bytes, uses a lot of disk space, and can take a while. So instead, in this d
1717
focusing on how to do these things more efficiently, copying only those parts of the partitions
1818
holding the data.
1919

20+
- If you have the overlay filesystem enabled, I suggest disabling it before pulling the images,
21+
just to make later configuration easier.
22+
23+
- If you flashed the SD card with the RPi Imager, then do the following on the RPi
24+
to prevent `cloud-init` from modifying the system configuration on the clones.
25+
- `sudo apt purge -y cloud-init && sudo apt autoremove -y`
26+
- `sudo rm -rvf /etc/cloud/ /var/lib/cloud/ /boot/firmware/{user-data,network-config}`
27+
28+
In the following, replace `/dev/sdX` with the source SD card's device name.
29+
2030
- Partition table:
2131
- Dump the table via: `sudo sfdisk -d /dev/sdX | tee part_table`
2232
- To prevent conflicts, remove unique identifiers via:
@@ -37,72 +47,84 @@ holding the data.
3747
- `sudo e2image -pra /dev/sdX2 sdX2.img`
3848
- `sudo e2image -pra /dev/sdX3 sdX3.img` (optional if you have a `data` partition)
3949

40-
- `sudo chown -c $USER:$USER *.img`
41-
- `chmod -c a-w *.img*`
50+
- Adjust permissions on image files:
51+
- `sudo chown -c $USER:$USER *.img*`
52+
- `chmod -c a-w *.img* part_table`
4253

4354
- If you want to back up these files into one:
4455
- `tar --create --file rpi-images.tar --verbose --sparse -- part_table sdX?.img*`
45-
- Alternatively, compressed, which can save a lot of space (in one test I ran, 50%):
56+
- Alternatively, compressed, which can save a lot of space (in a few tests I ran, over 50%):
4657
`tar --create --file rpi-images.tgz -I 'gzip -9' --verbose --sparse -- part_table sdX?.img`
4758
(in this case it's more efficient if you didn't compress the FAT backup above)
4859

4960

50-
⚠️ Untested Stuff Follows
51-
--------------------------
52-
53-
This document is a work in progress and I have not tested the following yet.
54-
55-
5661
Writing Images to an SD Card
5762
----------------------------
5863

5964
**⚠️ DANGER ZONE:** Make sure you're writing to the correct device!
6065

66+
Remove the original source SD card from your system, at least until new UUIDs are generated (below).
67+
In the following, replace `/dev/sdY` with the clone SD card's device name.
68+
6169
- Partition table:
62-
- `sudo sfdisk /dev/sdX < part_table`
63-
- TODO: I don't see the "data" label in the sfdisk output, do I need to do that manually?
70+
- `sudo sfdisk /dev/sdY < part_table`
6471

6572
- FAT Partition:
66-
- `zcat sdX1.img.gz | sudo dd of=/dev/sdX1 status=progress`
73+
- `zcat sdX1.img.gz | sudo dd of=/dev/sdY1 status=progress`
74+
- Alternatively, if it was uncompressed: `sudo dd if=sdX1.img of=/dev/sdY1 status=progress`
6775

6876
- ext4 Partitions:
69-
- `sudo e2image -pra sdX2.img /dev/sdX2`
70-
- `sudo e2image -pra sdX3.img /dev/sdX3` (optional)
77+
- `sudo e2image -pra sdX2.img /dev/sdY2`
78+
- `sudo e2image -pra sdX3.img /dev/sdY3` (optional)
7179

7280

7381
Post-Clone Updates
7482
------------------
7583

76-
Mount the system ext4 partition on a Linux system.
84+
**⚠️ DANGER ZONE:** Make sure you're writing to the correct device and directories!
85+
86+
In the following, replace `/dev/sdY` with the clone SD card's device name.
7787

78-
- TODO: `sudo lsblk -o+PARTUUID,UUID /dev/sdX` and `sudo tune2fs -U random /dev/sdX1` for `.../etc/fstab` update?
79-
- **and** adjust `root=PARTUUID=...` in `/boot/firmware/cmdline.txt` !
88+
- Check the partition IDs via `sudo lsblk -o+PARTUUID,UUID,LABEL /dev/sdY`,
89+
then generate new UUIDs as follows:
90+
- `sudo fatlabel -ir /dev/sdY1`
91+
- `sudo tune2fs -U random /dev/sdY2`
92+
- `sudo tune2fs -U random /dev/sdY3`
93+
- Note: In case you need to adjust partition labels later, see `e2label` and `fatlabel`.
94+
The usual partition labels are `bootfs` and `rootfs` (and the optional `data`).
8095

81-
- machine-id, Hostname
82-
- TODO: Test `sudo systemd-firstboot --root=/media/... --hostname=... --setup-machine-id --force`
83-
- TODO: Check that `/etc/hostname` *and* `/etc/hosts` were modified
96+
Mount the clone's FAT `bootfs` and ext4 `rootfs`. In the following, I will assume that
97+
they are mounted at `/media/USER/bootfs` and `/media/USER/rootfs`.
8498

85-
- SSH Host Keys
86-
- `sudo rm .../etc/ssh/ssh_host_*`
99+
- `sudo lsblk -o+PARTUUID /dev/sdY`, then:
100+
- `sudo vi /media/USER/bootfs/cmdline.txt` and edit `root=PARTUUID=...` to match the new `PARTUUID`
101+
- `sudo vi /media/USER/rootfs/etc/fstab` and edit all of the `PARTUUID=` fields to match the new `PARTUUID`s
87102

88-
- Bluetooth pairings
89-
- `sudo bash -c 'rm -rf .../var/lib/bluetooth/[0-9A-Fa-f]*'`
90-
(wildcard expansion needs to happen with root permissions)
103+
- `sudo ./clone-delete.sh /media/USER/rootfs` to delete various files that will be re-generated
91104

92-
- `sudo vi .../etc/machine-info` for `PRETTY_HOSTNAME` etc.
93-
- `sudo rm -rf .../var/log/journal/* .../var/log/* .../var/cache/* .../var/lib/systemd/random-seed`
105+
- machine-id and Hostname
106+
- `sudo systemd-firstboot --root=/media/USER/rootfs --hostname=HOSTNAME --setup-machine-id --force`
107+
- `sudo vi /media/USER/rootfs/etc/hosts` and replace all instances of the old hostname there too
94108

95-
- TODO: Test: if the processor architectures are the same,
96-
`sudo chroot /media/... /usr/sbin/make-ssl-cert generate-default-snakeoil --force-overwrite`
109+
- If you have this file, `sudo vi /media/USER/rootfs/etc/machine-info` and edit `PRETTY_HOSTNAME` etc.
97110

98111

99112
Post-Boot Updates on Clone
100113
--------------------------
101114

102-
- SSL Certs (if the processor architectures are different)
103-
- `sudo make-ssl-cert generate-default-snakeoil --force-overwrite`
104-
- SSH Keys should have been regenerated, but if you want to play it safe:
115+
- Regenerate SSH Host keys:
116+
- `sudo rm -vf /etc/ssh/ssh_host_*` (Note: The ssh service doesn't re-generate these files on boot,
117+
and will refuse to start if they're missing, which is why we don't do this until after the first
118+
boot of the clone.)
105119
- `sudo dpkg-reconfigure openssh-server`
120+
- `sudo systemctl restart ssh`
121+
122+
- SSL Certs - only if you had/have the package `ssl-cert` installed:
123+
- `sudo make-ssl-cert generate-default-snakeoil --force-overwrite`
124+
125+
- Do any other customization steps needed for your system here.
126+
127+
- If you had the overlay filesystem enabled, reenable it now.
106128

107129

108130
More Information
@@ -112,7 +134,7 @@ More Information
112134
- <https://wiki.archlinux.org/title/Disk_cloning#Versatile_cloning_solutions>
113135

114136

115-
<!-- spell: ignore PARTUUID blkid cmdline dhcpcd dpkg firstboot sfdisk zcat snakeoil -->
137+
<!-- spell: ignore PARTUUID blkid cmdline dhcpcd dpkg firstboot sfdisk zcat snakeoil fatlabel bootfs rootfs Imager autoremove -->
116138

117139
Author, Copyright, and License
118140
------------------------------

clone-delete.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
if [[ "$(id -u)" -ne 0 ]]; then
5+
echo "You need to be root."
6+
exit 1
7+
fi
8+
9+
usage() { echo "Usage: $0 ROOT_FS_PATH" 1>&2; exit 1; }
10+
[[ $# -eq 1 ]] || usage
11+
root_fs_path="$(realpath -s "$1")"
12+
13+
target="$(findmnt --all --first --noheadings --list --output TARGET --notruncate --target "$root_fs_path")"
14+
if [[ $target =~ ^/[^/]+ ]]; then
15+
source="$(findmnt --all --first --noheadings --list --output SOURCE --notruncate --target "$root_fs_path")"
16+
if [ ! -d "$root_fs_path/var" ]; then
17+
echo "Not a directory: $root_fs_path/var"
18+
exit 1
19+
fi
20+
else
21+
echo "Refusing to operate on target: $target"
22+
exit 1
23+
fi
24+
25+
# note the wildcard expansion in /var/lib/bluetooth needs root permissions
26+
to_delete_files=(
27+
"$root_fs_path"/var/lib/bluetooth/[0-9A-Fa-f]*
28+
"$root_fs_path"/var/log/*
29+
"$root_fs_path"/var/cache/*
30+
"$root_fs_path"/var/lib/systemd/random-seed
31+
)
32+
33+
echo -e "\nv WARNING v WARNING v WARNING v WARNING v WARNING v WARNING v WARNING v\n"
34+
echo "You are about to delete the following files"
35+
echo -n "from $source"
36+
echo -e " WITH ROOT PERMISSIONS.\n\nTo be deleted:"
37+
for elem in "${to_delete_files[@]}"; do echo "$elem"; done
38+
echo -e "\n^ WARNING ^ WARNING ^ WARNING ^ WARNING ^ WARNING ^ WARNING ^ WARNING ^\n"
39+
40+
read -n 1 -r -p "ARE YOU SURE? [yN] " REPLY
41+
echo -e "\n"
42+
if [[ $REPLY =~ ^[Yy]$ ]]
43+
then
44+
for elem in "${to_delete_files[@]}"; do rm -rvf "$elem"; done
45+
fi
46+
47+
# spell: ignore noheadings notruncate

0 commit comments

Comments
 (0)