What are Layers?
Layers are modular, composable components that define specific aspects of your Raspberry Pi build. Each layer encapsulates a specific and focussed piece of functionality and can declare dependencies on other layers, creating a flexible build system. Layers have been loosely grouped into categories with a super-set being as follows.
-
Device Layers: Hardware-specific configuration for a particular device type. For example, may only install firmware applicable to Pi5.
-
Image Layers: Image-specific configuration to produce an image with a defined footprint / on-disk layout. For example, an image capable of supporting AB booting for OS and system slot redundancy.
-
General Layers: Reusable configuration snippets, utilities, groups of related functionality, packages and/or scripts. For example, local system user account creation, basic networking, etc.
-
Suites: A layer providing a particular baseline of device functionality. Typically, these type of 'profile' layers are agnostic to the underlying device. Using a suite allows a single layer to recursively pull in other layers to provide a common base of functionality such as a desktop or SDK, or a minimal set of userland components providing apt and networking, container utilities, etc.
How are Layers Used?
rpi-image-gen uses custom metadata in a layer to declare variables and construct a deterministic layer build order then passes this to bdebstrap (https://github.com/bdrung/bdebstrap) which aggregates and merges everything together into a configuration. That configuration is handed off to mmdebstrap (https://gitlab.mister-muffin.de/josch/mmdebstrap) which is the engine that drives creation of the filesystem. By using bdebstrap, layers provide a structured way to configure and extend mmdebstrap’s capabilities:
Layer Structure
Each layer is a YAML file containing:
-
X-Env Metadata: Mandatory. Layer attributes, variable declarations, dependencies
-
mmdebstrap configuration: Optional. Package lists, repository mirrors, scripts
Build Process
-
Parameter Assembly: Config file, cmdline and CLI establish desired layers, environment variables and search paths
-
Layer Resolution: Layer dependencies determine build order
-
Variable Expansion: Environment variables are validated and expanded using defined rules
-
Configuration Merging: Layer are merged
-
mmdebstrap Execution: The merged configuration drives mmdebstrap to create the filesystem
-
Processing: Additional layer-specific scripts are executed at defined points, eg setup, essential, customize, cleanup, etc
-
Post-Processing: filesystem overlays, SBOM generation, image creation, hooks
Key Benefits of Layers
-
Modularity: Mix and match layers to create custom images
-
Reusability: Share common configurations across different builds
-
Validation: Built-in variable validation prevents configuration errors
-
Dependencies: Automatic resolution of layer prerequisites
-
Customisation: Override defaults through environment variables set via the config file
X-Env Metadata
Layers use a custom X-Env metadata schema that strictly follows the DEB822 format (https://repolib.readthedocs.io/en/latest/deb822-format.html) embedded within YAML comment blocks. The metadata is delimited by # METABEGIN and # METAEND markers and parsed using the standard python3-debian DEB822 parser. By using metadata fields a layer can define attributes, dependencies, and configuration variables. The metadata is parsed separately from the standard YAML content.
Metadata Structure
X-Env metadata is contained within comment blocks:
# METABEGIN
# X-Env-Layer-Name: This layer's name
# X-Env-Layer-Description: Brief description of what this layer does
# X-Env-Layer-Category: device
# X-Env-Layer-Requires: base-layer,required-layer
# X-Env-VarPrefix: device
# X-Env-Var-hostname: pi-${HOSTNAME_SUFFIX}
# X-Env-Var-hostname-Description: System hostname for the device
# X-Env-Var-hostname-Valid: regex:^[a-zA-Z0-9.-]+$
# X-Env-Var-hostname-Set: immediate
# METAEND
# mmdebstrap YAML configuration follows...
mmdebstrap:
packages:
- systemd
- network-manager
Static vs Dynamic Layers
Layers are static unless explicitly marked dynamic. To render a layer at build time add:
# X-Env-Layer-Type: dynamic
# X-Env-Layer-Generator: myscript
The loader then:
-
Checks for the existence of a temporary directory specified by tag
TMPROOT_layer=<tmpdir>on the layer search path (rpi-image-gendoes this automatically). -
Runs the generator with two arguments,
<template-path> <output-path>, where the output is written toTMPROOT_layer/path/to/<layer-name>.yaml. Thepath/toportion matches the layer’s path under its original search root, so dynamic layers mirror the same directory structure as static layers when reported via tagged paths. -
Reads all mmdebstrap/env content from the generated file while continuing to use the original
# X-Env-*metadata for naming, dependencies, documentation, etc.
Generators must write a complete layer to the output path. A trivial generator could copy the template directly to the output (e.g. cp works fine). More more complex generators may inject snapshot timestamps, merge user data, or pull content from other sources. If the generator writes nothing, the layer still 'exists' (metadata is intact) but it contains no YAML body information (packages, hooks, etc) and --describe output will reflect that. Dynamic layers provide a way to manipulate the layer YAML at build time. Modifications made by the generator only affect the YAML body. The original # X-Env-* metadata is always taken from the template.
|
Important
|
|
|
Note
|
|
Layer Attributes
X-Env-Layer-Name: Layer name
X-Env-Layer-Description: Human-readable description of the layer’s purpose
X-Env-Layer-Category: Categorisation (device, image, etc.)
X-Env-Layer-Version: Version identifier for the layer
X-Env-Layer-Requires: Comma-separated list of required layers
X-Env-Layer-Provides: Services or capabilities this layer provides
X-Env-Layer-RequiresProvider: Services or capabilities this layer requires
X-Env-Layer-Conflicts: Layers that cannot be used together with this one
X-Env-Layer-Type: Static (default) or dynamic
X-Env-Layer-Generator: If dynamic, the generator invoked by the loader
Dependencies and Providers
X-Env-Layer-Requires
-
Direct layer references: "I need these specific layers"
-
Concrete dependencies: Must reference actual layer names
-
Environment variable expansion: Supports
${VAR}syntax for dynamic dependencies -
Build order enforcement: Dependencies are loaded first and are pull in automatically
-
Example: A device layer depends on a device base-layer because the base-layer provides mandatory settings inherited by the device layer.
# METABEGIN
# X-Env-Layer-Name: conditional-layer
# X-Env-Layer-Requires: base-layer,${ARCH}-toolchain,${DISTRO}-packages
# METAEND
Environment variables in dependencies are evaluated during layer discovery using the current environment context. If a required environment variable is not set, layer discovery will fail.
This enables dynamic layer dependency resolution based on build-time variables such as:
-
Architecture:
${ARCH}-toolchainresolves toarm64-toolchainwhenARCH=arm64 -
Distribution:
${DISTRO}-packagesresolves todebian-packageswhenDISTRO=debian -
Build variant:
${VARIANT}-configfor different build configurations
|
Important
|
Only variables present in the environment can be used in dependencies. |
X-Env-Layer-Provides / X-Env-Layer-RequiresProvider
-
Abstract capability requirements: "I need something that provides X"
-
Service/capability contracts: Multiple layers could satisfy the requirement
-
Flexible implementation: Any layer providing the capability can fulfill it
-
Relationships: If a provider is required, only one can exist in the overall configuration
-
Example: A layer requires a device provider, which could be satisfied by different device layers
|
Important
|
Unlike dependencies, environment variables are not supported when evaluating providers. |
Environment Variables
X-Env-VarPrefix: Prefix for all variables declared by this layer (e.g., device)
X-Env-VarRequires: Comma-separated list of variables this layer expects from other layers
X-Env-Var-<name>: Variable declaration with default value (supports placeholders like ${DIRECTORY})
X-Env-Var-<name>-Description: Human-readable description of the variable
X-Env-Var-<name>-Valid: Validation rule (type, range, regex, enum, etc.)
X-Env-Var-<name>-Set: Set policy (immediate, lazy, force, skip)
Variable Naming Convention
Variables follow the pattern: IGconf_<prefix>_<name>
-
Layer declares:
X-Env-Var-hostnamewith prefixdevice -
Environment variable:
IGconf_device_hostname -
Template expansion: Can reference as
${IGconf_device_hostname}in YAML values
Placeholder Support
Variable values support dynamic placeholders:
${DIRECTORY}: Directory containing the layer file
${FILENAME}: Name of the layer file (without extension)
${FILEPATH}: Full path to the layer file
Configuration Variables
The environment variables declared by a layer customise build behavior:
-
Validation: Each variable includes validation rules (types, ranges, patterns)
-
Placeholders: Support for dynamic values like
${DIRECTORY}and${FILENAME} -
Set Policies: Control when and how variables are applied during layer resolution
-
Documentation: Integrated help and validation error messages
For variable validation help and policy explanations, use or refer to the variable-validation help page accessible via the individual layer documentation pages.rpi-image-gen metadata --help-validation
For detailed information about a particular layer, including configuration options and defaults, please inspect the layer via the command line () or refer to the layer’s documentation page. It is recommended to use a config file to set layer variables. Layers that declare variables specify a defined prefix. Use this prefix in the config file to set variables applicable to that layer. For example - device and image layers define variables with prefix 'device' and 'image' respectively:rpi-image-gen layer --describe <layer name>
device:
storage_type: nvme
image:
compression: zstd
Device and Image Layers
The config system allows device and image layers to be specified two different ways. Both yield the same result. The main difference is that the latter allows the name of the variable holding the device/image layer to be defined by the user, therefore making it customisable. Using the former makes more sense if defining other device settings since they can all be encapsulated under the same section in the config file.
device:
layer: rpi5
layer:
myvar: rpi5
The above would result in two variables being defined:
IGconf_device_layer=rpi5
IGconf_layer_myvar=rpi5
Both would pull layer into the system configuration.rpi5
rpi-image-gen expands and references all variables at layer collection time, whereas it looks specifically for IGconf_layer_* and IGconf_device_layer to locate device and image layers respectively for those particular sections.IGconf_image_layer
It’s worth noting that rpi-image-gen does not mandate a device or image layer being specified. The construction of a filesystem can take place with or without either of these. For example, a user may wish to use rpi-image-gen to create a filesystem tar ball for use in a docker container.
Specifying Layers
Layers can be specified in the config file by name (i.e. their ). The name of the variable containing the layer name is completely arbitrary.X-Env-Layer-Name
layer:
foo: trixie-minbase
bar: rpi5
app: my-app
This would result in the following variables being defined:
IGconf_layer_foo=trixie-minbase
IGconf_layer_bar=rpi5
IGconf_layer_app=my-app
rpi-image-gen would attempt to locate layers ,trixie-minbase and rpi5, including their depdendencies. Deduplication of layers occurs at the resolution phase, meaning that specifying duplicate layer names is harmless and basically a nop.my-app
How to Use This Documentation
-
Browse the auto-generated layer list (currently only available in the HTML documentation) to find layers relevant to your build
-
Click on any layer name to view detailed documentation information including:
-
Configuration variables and their validation rules
-
Package dependencies and installation details
-
Layer relationships and dependencies
-
Technical implementation details and companion information
Getting Started
-
Choose a device layer that matches your Raspberry Pi hardware
-
Choose an image layer applicable to your deployment
-
Add a suite and/or list of general layers for additional functionality
-
Configure the variables as documented in each layer
-
Run
rpi-image-gen buildwith your config