Bootstrapping
During the production and provisioning of devices, a standard image is usually flashed onto each device. This image is identical across all devices to maintain consistency and simplify the provisioning process. After flashing the image, each device is then typically booted for the first time as part of an end-of-line configuration and quality assurance testing procedure. This initial boot process can also be used for setting up and initializing device-specific configurations, a process to which we refer as bootstrapping.
The bootstrapping process can include a variety of steps such as:
- Growing the partition table to utilize the full storage capacity of the device.
- Creating necessary partitions and filesystems that are not part of the image.
- Initializing on-device chips and one-time-programmable storage.
- Generating unique device identifiers and security certificates.
- Customizing configurations to suit device-specific requirements.
When configured to run before the init system, Rugix Ctrl carries out bootstrapping itself. By default, it grows the partition table and creates the data partition that state management lives on; you can extend it with bootstrapping hooks. Bootstrapping runs this early because the rest of the boot, state management in particular, depends on the data partition existing.
System Layout
While one may choose to already prepare the provisioning image and its partition table for the exact storage capacity of a device, this may not always be feasible. Maybe you have different variants of a device with different storage capacities and want to use the same image for all of them, or you may want your users to be able to flash an image onto their own storage media. In these cases, the partition table of the image will not match the storage capacity and typically does not even contain all the required partitions, such as redundant system partitions and the data partition. In that case, you can configure Rugix Ctrl to adapt the partition table and create the necessary partitions and filesystems. The desired system layout is defined in the layout configuration section.
Custom Layouts
Rugix Ctrl supports MBR (DOS) partition tables and GPT partition tables. Here are two example layouts:
[layout]
type = "mbr"
partitions = [
{ name = "config", size = "256MiB", type = "0c" },
{ name = "boot-a", size = "128MiB", type = "0c" },
{ name = "boot-b", size = "128MiB", type = "0c" },
{ name = "extended", type = "05" },
{ name = "system-a", size = "6GiB" },
{ name = "system-b", size = "6GiB" },
{ name = "data", filesystem = { type = "ext4" } },
]
[layout]
type = "gpt"
partitions = [
{ name = "EFI", size = "256MiB", type = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" },
{ name = "boot-a", size = "256MiB" },
{ name = "boot-b", size = "256MiB" },
{ name = "system-a", size = "6GiB" },
{ name = "system-b", size = "6GiB" },
{ name = "data", filesystem = { type = "ext4" } },
]
Both layouts prepare the system for an A/B update setup with two boot and two system partitions. The last partition is not required to have a size, in which case, it will take up the entire remaining space. So, in case of the examples, the data partition will take up all the remaining space. In case of an MBR partition table, the extended partition also does not require a size and will extend to the end of the disk. The filesystem option is optional and will cause Rugix Ctrl to create a filesystem. The type option is also optional defaulting to 83 and 0FC63DAF-8483-4772-8E79-3D69D8477DE4 for MBR and GPT, respectively.
Default Layout
For a typical system, you would use one of the layouts above. As a shortcut, the type of the layout can be set to default. In that case, only a system-size needs to be configured. Rugix Ctrl will then automatically create a matching partition table based on the type of table it finds on the root disk and the configured system size. Here is an example:
[layout]
type = "default"
system-size = "4GiB"
Disable Partition Creation
If you do not want Rugix Ctrl to create any partitions, simply set the layout’s type to none:
[layout]
type = "none"
Hooks
Hooks let you adapt the bootstrapping and boot process.
For bootstrapping, the stages of bootstrap hooks are:
prepare: Runs after mounting the config partition and determining that the system should be bootstrapped.pre-layout: Runs directly before applying the system partition layout.post-layout: Runs directly after applying the system partition layout.
For the boot process itself, the boot hooks are:
pre-init: Runs early, before the system is initialized.post-init: Runs after the system has been initialized, just before Rugix Ctrl hands control to the actual init system, such as systemd.
A failure in a boot hook is logged but does not abort the boot.
All of these stages run during boot, before the init system. For hooks running that early, you can assume the following environment:
/is mounted read-only to the respective root filesystem./sys,/proc, and/devare mounted./runis mounted to a temporary, in-memory filesystem and is passed through to the final system. If you need to communicate something to processes that run after boot, place it in/run.
In addition, the config partition is mounted read-only, usually at /run/rugix/mounts/config.
Configuration Reference
Here is the complete schema for the bootstrapping configuration file:
BootstrappingConfig
Bootstrapping configuration.
Fields (JSON object)
disabledoptionalDisable bootstrapping altogether.
boolean layoutoptionalSystem layout configuration to use for bootstrapping.
SystemLayoutConfig
Cases internally — tag field
typeMbr→"mbr"MBR partition layout.
{ "type": "mbr", ...<payload fields> } — or "content" for non-object payloadsPartitionLayoutConfig
Fields (JSON object)
partitionsrequiredPartitions of the layout.
array<LayoutPartitionConfig>
Items
LayoutPartitionConfig
Fields (JSON object)
nameoptionalOptional name of the partition.
string
sizeoptionalSize of the partition.
NumBytes
type(fromty) optionalType of the partition (one byte hex or GUID).
PartitionType
filesystemoptionalFilesystem of the partition.
Filesystem
Cases internally — tag field
typeExt4→"ext4"{ "type": "ext4", ...<payload fields> } — or "content" for non-object payloadsExt4Filesystem
Fields (JSON object)
labeloptionalstring
additional-options(fromadditional_options) optionalarray<string>
Items
string
Gpt→"gpt"GPT partition layout.
{ "type": "gpt", ...<payload fields> } — or "content" for non-object payloadsPartitionLayoutConfig
Fields (JSON object)
partitionsrequiredPartitions of the layout.
array<LayoutPartitionConfig>
Items
LayoutPartitionConfig
Fields (JSON object)
nameoptionalOptional name of the partition.
string
sizeoptionalSize of the partition.
NumBytes
type(fromty) optionalType of the partition (one byte hex or GUID).
PartitionType
filesystemoptionalFilesystem of the partition.
Filesystem
Cases internally — tag field
typeExt4→"ext4"{ "type": "ext4", ...<payload fields> } — or "content" for non-object payloadsExt4Filesystem
Fields (JSON object)
labeloptionalstring
additional-options(fromadditional_options) optionalarray<string>
Items
string
Default→"default"Default partition layout.
{ "type": "default", ...<payload fields> } — or "content" for non-object payloadsDefaultLayoutConfig
Fields (JSON object)
system-size(fromsystem_size) requiredSize of the system partitions.
NumBytes
None→"none"No partition layout.
{ "type": "none" }
The schema is defined in bootstrapping.sidex.
Footnotes
-
Rugix Ctrl also accepts the marker at
/rugix/bootstrap(without the leading dot) for environments where dotfiles are inconvenient to ship, most notably wic’sbootimg-partitionplugin, whosemcopyinvocation skips dotfiles in the FAT root. ↩ -
A malicious actor with write access to the config partition could re-arm bootstrapping on a production device, even with secure boot in place. To mitigate this, install a
preparebootstrapping hook that gates the process on an independent signal (e.g., a flag in one-time-programmable memory) and aborts if it should not run. ↩