# How to edit cloud-init files on the device or on an image file The cloud-init **NoCloud** files live on the **boot partition**. On the running device they are at: | File | Path on device | |------|----------------| | **user-data** | `/boot/firmware/user-data` | | **meta-data** | `/boot/firmware/meta-data` | | **network-config** | `/boot/firmware/network-config` | They are owned by **root** and need **sudo** to edit. --- ## Method 1: Edit on the device over SSH 1. **SSH into the device** (e.g. via jump host): ```bash ssh -o ConnectTimeout=10 -J root@10.20.30.153 pi@10.20.50.147 ``` 2. **Edit with nano** (or `vi` if you prefer): ```bash sudo nano /boot/firmware/user-data sudo nano /boot/firmware/meta-data sudo nano /boot/firmware/network-config ``` 3. **Save and exit:** in nano, `Ctrl+O` then Enter to save, `Ctrl+X` to exit. 4. **YAML:** use **spaces only** for indentation (no tabs). Wrong indentation can break cloud-init. --- ## Method 2: Copy to your PC, edit, copy back 1. **Copy from device to your PC** (from your project machine): ```bash scp -o ConnectTimeout=10 -J root@10.20.30.153 \ pi@10.20.50.147:/boot/firmware/user-data ./user-data scp -o ConnectTimeout=10 -J root@10.20.30.153 \ pi@10.20.50.147:/boot/firmware/meta-data ./meta-data scp -o ConnectTimeout=10 -J root@10.20.30.153 \ pi@10.20.50.147:/boot/firmware/network-config ./network-config ``` If you get "Permission denied" reading from `/boot/firmware/`, on the device run: `sudo cp /boot/firmware/user-data /boot/firmware/meta-data /boot/firmware/network-config /tmp/ && sudo chmod 644 /tmp/user-data /tmp/meta-data /tmp/network-config` then from your PC: `scp -J root@10.20.30.153 pi@10.20.50.147:/tmp/user-data ./user-data` (and same for meta-data, network-config). 2. **Edit** `user-data`, `meta-data`, and `network-config` on your PC. 3. **Copy back** (push from PC to device). Because the destination is root-owned, use a two-step on the device, or use root SSH: **Option A – copy to home then move with sudo:** ```bash # On your PC: copy to pi's home scp -o ConnectTimeout=10 -J root@10.20.30.153 ./user-data ./meta-data ./network-config \ pi@10.20.50.147:~/ # Then on the device (SSH in as pi): sudo cp ~/user-data ~/meta-data ~/network-config /boot/firmware/ sudo chmod 644 /boot/firmware/user-data /boot/firmware/meta-data /boot/firmware/network-config ``` **Option B – if you have root SSH to the device:** ```bash scp -o ConnectTimeout=10 -J root@10.20.30.153 ./user-data root@10.20.50.147:/boot/firmware/ scp -o ConnectTimeout=10 -J root@10.20.30.153 ./meta-data root@10.20.50.147:/boot/firmware/ scp -o ConnectTimeout=10 -J root@10.20.30.153 ./network-config root@10.20.50.147:/boot/firmware/ ``` --- ## Method 3: Edit on an image file (.img or .img.xz) To edit cloud-init **inside a disk image** (e.g. `gnss-bootstrap-20260223-215010.img.xz`) without booting the device: 1. **Run the helper script** (requires `sudo` for loop device and mount): ```bash cd /path/to/reTerminal-DM4 ./emmc-provisioning/scripts/edit-cloudinit-on-image.sh gnss-bootstrap-20260223-215010.img.xz ``` 2. The script will: - Decompress the `.img.xz` (if needed; allow ~3GB free space and a minute or two) - Attach the image with `losetup` and mount the **boot** (FAT32) partition - Open your `$EDITOR` (or `nano`) on `user-data`, `meta-data`, and `network-config` - After you save and exit, unmount and recompress, **overwriting** the original `.img.xz` 3. **Back up the image** before running if you want to keep the original: ```bash cp gnss-bootstrap-20260223-215010.img.xz gnss-bootstrap-20260223-215010.img.xz.bak ``` 4. **Options:** - `--no-recompress` — Leave the image decompressed after editing (do not overwrite the `.img.xz`). - `--replace-with-repo` — Copy `user-data`, `meta-data`, and `network-config` from `emmc-provisioning/cloud-init/` into the image before opening the editor (useful to start from repo templates). --- ## What to edit (typical) - **meta-data** - `instance_id`: change if you want cloud-init to treat this as a new instance (e.g. per device). - Add `local-hostname: guard` (or your hostname) so the hostname is set on first boot. - **user-data** - Uncomment and set `hostname: ...` if you don’t use meta-data hostname. - To run your **first-boot script** from the provisioning server, add a `runcmd` section that downloads and runs it (see `cloud-init/user-data-remote-gnss.example` in the repo). - In that runcmd, set the **FILE_SERVER** URL to match your deployment network (e.g. `http://10.20.50.1:5000/files/first-boot` or your LXC IP). - **network-config** - Uncomment and adjust if you need static IP or specific WiFi; otherwise DHCP is usually enough. --- ## Example: minimal user-data that runs first-boot from your server You can replace (or add to) the default template with something like this, and adjust the URL to your file server: ```yaml #cloud-config package_update: true package_upgrade: false packages: [curl] runcmd: - curl -fsSL "http://10.20.50.1:5000/files/first-boot.sh" -o /tmp/first-boot.sh - curl -fsSL "http://10.20.50.1:5000/files/first-boot.conf" -o /tmp/first-boot.conf - chmod +x /tmp/first-boot.sh - /tmp/first-boot.sh ``` Full example with user, SSH, and optional first-boot config: **emmc-provisioning/cloud-init/user-data-remote-gnss.example**. --- ## After editing - No reboot needed for the edits to “take effect”; they are just files on the boot partition. - When you **capture the image** (backup), the boot partition is included, so the updated `user-data`, `meta-data`, and `network-config` will be on the golden image. - On **first boot** after deploy, cloud-init reads these files and runs accordingly.