Version=1
Path=/var/lib/myapp
Path=/etc/myapp/state
Immutable GPT A/B layout for rotational OTA updates, boot/system redundancy, and a shared persistent data partition.
This system uses an A/B, read‑only root with all writable state on a single persistent partition.
| Mount point | Backing | Type | Notes |
|---|---|---|---|
/ |
/dev/disk/by-slot/active/system |
erofs or ext4 |
ro System root (active slot A or B) |
/boot/firmware |
/dev/disk/by-slot/active/boot |
vfat |
ro Boot files (active slot A or B) |
/bootfs |
BOOTFS |
vfat |
rw Boot metadata |
/persistent |
PERSISTENT |
ext4 |
rw Shared persistent storage |
/home |
/persistent/home |
bind |
User data shared across slots |
/var |
/persistent/slots/<slot>/var |
bind |
Per‑slot runtime state (systemd, caches, etc.) |
/var/log/journal |
/persistent/log/journal |
bind |
Single log directory used by both slots |
<slot-shared paths> |
/persistent/shared/<path> |
bind |
Application data shared across slots (layer-declared, see Slot-shared application data) |
Rationale (immutable root + A/B)
Supports delta/incremental OTA updates by treating root as a static image.
Reliable rollbacks: slot can be flipped if a new root fails health checks.
Reduced write amplification and storage wear, clearer separation of state.
Predictable per‑slot state in /var.
Shared /home for slot agnostic user storage.
Shared journalling: centralised point for device logging.
Preserves SBOM accuracy: executing software matches the manifest exactly.
Blocks on-device package installs, preventing SBOM drift.
Enables auditable, reproducible releases and stronger supply-chain assurances.
Logging
A single persistent journal directory at /persistent/log/journal stores logs from either slot.
Journaling is configured for endurance and reliability.
Machine Identity (systemd)
/etc/machine-id is synchronised early with /persistent/common/etc/machine-id using a oneshot unit.
|
Warning
|
Slot partition GPT labels are mandatory to associate the immutable root with its matching persistent storage.
If slot GPT labels are missing/duplicated, If slot GPT labels can’t be guaranteed, this layout is not suitable for your device. |
Any layer can declare filesystem paths whose application data should be shared across slots. At boot, each declared path is bind-mounted from a common location on the persistent partition so the same data is visible regardless of which slot is active.
slot-shared-generator runs at early boot, reads every /etc/rpi-image-gen/slot-shared.d/*.conf file and emits a .mount systemd unit for each declared path, mounting /persistent/shared/<path> over <path> as a bind mount.
persistent-shared-init.service runs before the bind mounts activate. It creates any missing /persistent/shared/<path> directories on the persistent partition, handling first boot and disaster-recovery scenarios where the partition has been re-initialised.
Bind mounts are guarded by ConditionPathIsDirectory on the source. If the directory is absent the mount is skipped and the service falls back to its local path - safe degradation with no boot failure.
Drop a .conf file into /etc/rpi-image-gen/slot-shared.d/, eg via the layer’s rootfs overlay or script:
Version=1
Path=/var/lib/myapp
Path=/etc/myapp/state
Version=1Required. A generator that does not recognise the version skips the file with a warning rather than misprocessing it.
Path=One or more absolute paths to share. Multiple Path= entries are supported in a single file. A missing leading / is corrected automatically. Only directory paths are supported.
Unknown keys are silently ignored, providing forward compatibility within version 1. A version increment signals a breaking change. The files are inert in non-AB configurations.
Shared data is stored under /persistent/shared/, mirroring the full path hierarchy:
/persistent/
└── shared/
└── var/
└── lib/
└── myapp/ <-- bind-mounted over /var/lib/myapp
Because the immutable root retains a copy of each declared path, files baked into the image are pushed into the shared location by rpi-persistent-shared-init, before the bind mounts activate. Files are only pushed if missing from or different to the destination. This means an OTA update can deliver new or updated shared application data simply by including it in the rootfs - no separate data partition update is required.
Handled entirely by layer rpi-ab-slot-mapper.
This layer generates an image which has bit-for-bit identical A/B slot members:
Both a.boot and b.boot contain the exact same vfat filesystem.
Both a.system and b.system contain the exact same ext4 filesystem.
Filesystem UUIDs are intentionally identical across slots.
GPT partition GUIDs (PARTUUID) are unique per partition.
GPT partition labels (PARTLABEL) are unique per partition.
Faster image creation: only a single slot pair is produced.
Simpler updates: only a single slot pair is built, tested, and distributed.
Update generation: operating on a single slot pair is unambiguous.
Smaller CI/storage footprint: fewer images to store.
Does not use UUID= anywhere. Duplicate filesystem UUIDs make /dev/disk/by-uuid/* ambiguous.
Uses stable identifiers instead, e.g. /dev/disk/by-slot/{active,other}/{boot,system}.
Identical content inside LUKS is supported. Slot mapping can use GPT labels or static map triplets to identify mapper:<name>:<part> slot components.
References:
IGconf_device_storage_type,
IGconf_device_class
Declares (prefix: image):
| Variable | Description | Default | Validation | Policy |
|---|---|---|---|---|
IGconf_image_boot_part_size |
Boot partition size per-slot. |
96M
|
Size value with optional unit (bytes, k/m/g/s) or percentage | immediate |
IGconf_image_system_part_size |
System partition size per-slot. |
512M
|
Size value with optional unit (bytes, k/m/g/s) or percentage | immediate |
IGconf_image_data_part_size |
Writable data partition retained across slot rotations. |
1G
|
Size value with optional unit (bytes, k/m/g/s) or percentage | immediate |
IGconf_image_rootfs_type |
Root filesystem type for each system partition. |
erofs
|
Must be one of: ext4, erofs | immediate |
IGconf_image_assetdir |
Image specific asset directory |
${DIRECTORY}
|
Non-empty string value | immediate |
IGconf_image_pmap |
Provisioning Map type for this image layout. clear: All partitions will be provisioned unencrypted. crypt: All non-boot partitions will be provisioned encrypted. cryptslots: Only system OS partitions will be provisioned encrypted. cryptdata: Only the data partition will be provisioned encrypted. |
clear
|
Must be one of: clear, crypt, cryptslots, cryptdata | immediate |
IGconf_image_ptable_protect |
Enable eMMC Power-On Write Protect of the partition table. If enabled, the first 8MB of the boot storage device will be protected. Applies to eMMC only. |
n
|
Boolean value - accepts: true/false, 1/0, yes/no, y/n (case insensitive) | lazy |
IGconf_image_compression |
Compression scheme used for update payloads. |
zstd
|
Non-empty string value | immediate |
Installs:
dosfstoolse2fsprogsinitramfs-toolsrsyncFile: gpt/ab_userdata/image.yaml
Type: static