# Explicit WKS Partitioning for Mender A/B Updates

**URL:** <https://hub.mender.io/t/explicit-wks-partitioning-for-mender-a-b-updates/8162>\
**Category:** Yocto Project\
**Tags:** partitioning\
**Created:** [January 12, 2026, 9:08am UTC](https://hub.mender.io/t/explicit-wks-partitioning-for-mender-a-b-updates/8162 "2026-01-12T09:08:51Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![TheYoctoJester](https://yyz2.discourse-cdn.com/flex036/user_avatar/hub.mender.io/theyoctojester/32/1444_2.png) [@TheYoctoJester](https://hub.mender.io/u/TheYoctoJester)\
**Post date:** [January 12, 2026, 9:08am UTC](https://hub.mender.io/t/explicit-wks-partitioning-for-mender-a-b-updates/8162/1 "2026-01-12T09:08:51Z")

</div>

Mender’s integration into Yocto typically relies on dynamic image generation through classes like `mender-image` and `mender-part-images`. These classes automatically create the required partition layout and generate both disk images and OTA artifacts. While convenient, this abstraction can become limiting when you need precise control over your partition layout, want to understand exactly what’s happening during image generation, or are porting to hardware with unusual boot requirements.

This tutorial demonstrates an alternative approach: using explicit WKS (Wic Kickstart) files to define the Mender partition layout directly. The reference implementation targets Raspberry Pi 4 but the concepts apply to any platform.

## Prerequisites

This tutorial assumes you’re comfortable with:

- Yocto layer structure and recipe syntax
- Basic [WIC concepts and `.wks` file format](https://docs.yoctoproject.org/ref-manual/kickstart.html)
- [KAS build configurations](https://kas.readthedocs.io/)
- [Mender A/B update concepts](https://docs.mender.io/overview/introduction)

## Bootloader Integration

Mender’s A/B update mechanism requires bootloader cooperation. After the Mender client writes a new rootfs to the inactive partition, the bootloader must:

1. Switch to booting from the newly written partition
2. Track boot attempts and detect failures
3. Roll back to the previous partition if the new system fails to boot

Mender supports several [bootloader integrations](https://docs.mender.io/operating-system-updates-yocto-project/board-integration/bootloader-support) out of the box: [U-Boot](https://docs.mender.io/operating-system-updates-yocto-project/board-integration/bootloader-support/u-boot), [GRUB](https://docs.mender.io/operating-system-updates-yocto-project/board-integration/bootloader-support/grub), and UEFI. Custom integrations are possible for other bootloaders. This tutorial uses U-Boot as the example since it’s common on embedded ARM platforms like the Raspberry Pi, but the WKS partitioning concepts apply regardless of which bootloader you use.

The bootloader integration determines how boot state is stored and how the WKS file must accommodate it. With U-Boot, this means a raw environment block before the partition table (see [manual U-Boot integration](https://docs.mender.io/operating-system-updates-yocto-project/board-integration/bootloader-support/u-boot/manual-u-boot-integration) for details). With GRUB, it might be an EFI system partition with `grubenv`. Your WKS file must match your chosen bootloader’s requirements.

## The Mender Partition Layout

A Mender-enabled system requires several distinct storage areas. Note that “partition” here refers to the logical concept of reserved storage - the actual implementation varies by platform. Some areas may be true partitions, others may be raw regions at fixed offsets, and some may share space within a larger partition.

1. **Bootloader and boot state storage** - Where the bootloader binary, configuration, and A/B switching state reside. This is highly platform-specific: it might be a FAT32 boot partition, an EFI system partition, raw storage at fixed offsets, or a combination. The key requirement is that the bootloader can determine which root partition to boot and track update state.

2. **Root filesystem A** - The active root. This is where the system boots from initially.

3. **Root filesystem B** - The inactive root. Mender writes new rootfs images here during updates, then the bootloader switches to boot from this partition.

4. **Data partition** - Persistent across updates. Holds Mender state, device identity, and application data that should survive updates.

The critical constraint: Root A and Root B must be exactly the same size. Mender uses raw block copying for updates, so size mismatches cause failures.

## The WKS File

The [OpenEmbedded Kickstart (.wks) format](https://docs.yoctoproject.org/ref-manual/kickstart.html) defines partition layouts for WIC image generation. Here’s the complete WKS file for Raspberry Pi 4 ([`mender-explicit-raspberrypi4-64.wks`](https://github.com/mendersoftware/meta-mender-community/blob/scarthgap/kas/demos/meta-mender-explicit-wic/wic/mender-explicit-raspberrypi4-64.wks)):

```wks
# U-Boot environment - raw storage for A/B boot state (not a partition)
# Other bootloaders handle this differently; this is Raspberry Pi + U-Boot specific
part --source rawcopy --sourceparams="file=uboot.env" --ondisk mmcblk0 --align 8192 --no-table

# Boot storage (100MB, FAT32) - kernel, device tree, U-Boot binary
# This is a true partition on RPi; other platforms may differ
part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 8192 --size 100

# Root filesystem A (512MB, ext4, active)
# Standard rootfs source - this is the active root partition for booting
part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --label rootA --align 8192 --fixed-size 512M

# Root filesystem B (512MB, ext4, inactive)
# Empty partition with same size as Root A - used for A/B updates
# Mender client will write new rootfs here during updates
part --ondisk mmcblk0 --fstype=ext4 --label rootB --align 8192 --fixed-size 512M

# Data partition (128MB, ext4, persistent)
# Uses rootfs source with change-directory=data to populate with /data contents
part /data --source rootfs --change-directory=data --ondisk mmcblk0 --fstype=ext4 --label data --align 8192 --size 128

# Use MBR partition table (compatible with Raspberry Pi bootloader)
bootloader --ptable msdos

```

Let’s examine the key elements:

**Bootloader state (U-Boot specific)**: The `--no-table` flag places this data at a raw offset without a partition table entry. The `--source rawcopy` with `file=uboot.env` copies the U-Boot environment file directly. This file contains the variables that decide whether to boot from Root A or Root B, along with retry counters for rollback logic. This is one way to handle boot state - other bootloaders store it differently. GRUB uses `grubenv` within a partition, some platforms use dedicated EEPROM, and others embed state in the bootloader binary itself.

**Boot partition** : In this Raspberry Pi example, a dedicated FAT32 partition holds the kernel, device tree, and U-Boot binary. The `--source bootimg-partition` source is a standard WIC plugin that collects files from `IMAGE_BOOT_FILES` and combines them with boot-related files from the rootfs. The `--active` flag marks this partition as bootable in the MBR. On other platforms, boot files might live in an EFI system partition, be loaded directly from raw flash, or reside elsewhere entirely - adapt according to your hardware’s boot flow.

**Root partitions** : Both use `--fixed-size 512M` to ensure identical sizes. Root A uses `--source rootfs` to populate it with the filesystem contents. Root B has no source - it’s created empty. During an OTA update, Mender writes the new rootfs here.

**Data partition** : The `--change-directory=data` option is the key trick here. Instead of copying the entire rootfs, WIC only copies contents from the `/data` directory within the rootfs. This mirrors what `mender-dataimg.bbclass` does.

**Partition table** : Raspberry Pi requires MBR format (`--ptable msdos`). Other platforms may need GPT.

### The Dual-Root Variant

For development and testing, a [second WKS file](https://github.com/mendersoftware/meta-mender-community/blob/scarthgap/kas/demos/meta-mender-explicit-wic/wic/mender-explicit-raspberrypi4-64-dual-root.wks) populates both root partitions:

```wks
# Root filesystem B (512MB, ext4, inactive)
# Also populated with rootfs content - same as Root A for immediate A/B testing
part --source rootfs --ondisk mmcblk0 --fstype=ext4 --label rootB --align 8192 --fixed-size 512M

```

The only difference is adding `--source rootfs` to the Root B partition. This creates a larger image but allows immediate A/B switching tests without performing an OTA update first.

## The `.bbclass`

The [`mender-explicit-wic.bbclass`](https://github.com/mendersoftware/meta-mender-community/blob/scarthgap/kas/demos/meta-mender-explicit-wic/classes/mender-explicit-wic.bbclass) handles the integration between Mender and WIC:

```bitbake
# Inherit essential Mender functionality but NOT image generation classes
# Note: mender-uboot is specific to U-Boot integration; use mender-grub for GRUB-based systems
inherit mender-setup mender-systemd mender-uboot

# Enable only essential Mender features (no dynamic image generation)
# The mender-uboot feature is bootloader-specific; substitute mender-grub for GRUB systems
MENDER_FEATURES_ENABLE:append = " \
    mender-uboot \
    mender-auth-install \
    mender-update-install \
    mender-systemd \
    mender-growfs-data \
"

# Explicitly disable dynamic partitioning features
MENDER_FEATURES_DISABLE:append = " mender-image mender-part-images"

```

This is the critical part: we inherit the Mender setup classes for configuration variables and bootloader integration, but explicitly disable `mender-image` and `mender-part-images`. These are the classes that would normally generate the partition layout dynamically.

The bootloader class (`mender-uboot` in this example) handles the specifics of A/B boot switching. For GRUB-based systems, you would inherit `mender-grub` instead and enable the `mender-grub` feature. The WKS file would also need adjustment to match the bootloader’s expectations.

The WKS file selection uses a machine-specific pattern:

```bitbake
WKS_FILE = "mender-explicit-${MACHINE}.wks"
WKS_SEARCH_PATH:prepend = "${LAYERDIR_meta-mender-explicit-wic}/wic:"

```

This allows adding support for additional machines by creating appropriately named WKS files.

### Artifact Generation

Since we disabled `mender-image`, we need our own `.mender` artifact generation:

```bitbake
IMAGE_FSTYPES:append = " wic wic.bz2 mender"

IMAGE_CMD:mender() {
    if [-z "${MENDER_ARTIFACT_NAME}"]; then
        bbfatal "MENDER_ARTIFACT_NAME not set."
    fi

    ROOTFS_IMG="${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${ARTIFACTIMG_FSTYPE}"
    MENDER_ARTIFACT="${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.mender"

    DEVICE_TYPE_ARGS=""
    for DEVICE_TYPE in ${MENDER_DEVICE_TYPES_COMPATIBLE}; do
        DEVICE_TYPE_ARGS="${DEVICE_TYPE_ARGS} -t ${DEVICE_TYPE}"
    done

    mender-artifact write rootfs-image \
        --artifact-name ${MENDER_ARTIFACT_NAME} \
        ${DEVICE_TYPE_ARGS} \
        --file ${ROOTFS_IMG} \
        --output-path ${MENDER_ARTIFACT}
}

do_image_mender[depends] += " \
    mender-artifact-native:do_populate_sysroot \
    ${PN}:do_image_${ARTIFACTIMG_FSTYPE} \
"

```

This custom `IMAGE_CMD:mender` function takes the ext4 rootfs image and wraps it into a `.mender` artifact using [`mender-artifact`](https://docs.mender.io/artifact-creation/create-an-artifact). The artifact can then be deployed via Mender server for OTA updates.

## The Image Recipe

The [image recipe](https://github.com/mendersoftware/meta-mender-community/blob/scarthgap/kas/demos/meta-mender-explicit-wic/recipes-core/images/mender-explicit-demo-image.bb) ties everything together:

```bitbake
SUMMARY = "Mender A/B demo image using explicit WIC partitioning"

inherit core-image mender-explicit-wic

IMAGE_INSTALL = "\
    packagegroup-core-boot \
    ${CORE_IMAGE_EXTRA_INSTALL} \
    mender-auth \
    mender-update \
    mender-configure \
    mender-connect \
    kernel-image \
    kernel-devicetree \
"

ROOTFS_POSTPROCESS_COMMAND += "create_mender_data_structure; "

create_mender_data_structure() {
    install -d ${IMAGE_ROOTFS}/data
    install -d ${IMAGE_ROOTFS}/data/mender
    install -d ${IMAGE_ROOTFS}/data/mender-configure
}

```

The `create_mender_data_structure` function creates the `/data` directory structure in the rootfs. WIC then picks up these contents via the `--change-directory=data` directive in the WKS file to populate the data partition.

## KAS Configuration

[KAS](https://github.com/siemens/kas) is a build tool that simplifies Yocto project setup. The configuration file [`raspberrypi4-64-mender-explicit-wic.yml`](https://github.com/mendersoftware/meta-mender-community/blob/scarthgap/kas/demos/raspberrypi4-64-mender-explicit-wic.yml) assembles all required layers:

```yaml
header:
  version: 14

machine: raspberrypi4-64
distro: poky

repos:
  poky:
    url: git://git.yoctoproject.org/poky
    commit: c162696dae5798e2ab1198403d0bc1d65d64068d
    layers:
      meta:
      meta-poky:
      meta-yocto-bsp:

  meta-openembedded:
    url: git://git.openembedded.org/meta-openembedded
    commit: e92d0173a80ea7592c866618ef5293203c50544c
    layers:
      meta-oe:

  meta-mender:
    url: https://github.com/mendersoftware/meta-mender.git
    commit: 0d393c2267a92091f975eb932338c07109f5f5fd
    layers:
      meta-mender-core:

  meta-raspberrypi:
    url: https://github.com/agherzan/meta-raspberrypi.git
    commit: 1091bde25e9ebaea33114edb85e4aee931d105f3

  meta-mender-community:
    layers:
      meta-mender-raspberrypi:
      kas/demos/meta-mender-explicit-wic:

local_conf_header:
  mender-artifact: |
    MENDER_ARTIFACT_NAME = "explicit-wic"
    IMAGE_FSTYPES:prepend = "ext4 "

  raspberrypi: |
    RPI_USE_U_BOOT = "1"
    ENABLE_UART = "1"
    MENDER_BOOT_PART_SIZE_MB = "100"
    IMAGE_FSTYPES:remove = " rpi-sdimg"

  mender-explicit-demo: |
    MENDER_FEATURES_ENABLE:append = " \
        mender-uboot \
        mender-auth-install \
        mender-update-install \
        mender-systemd \
        mender-growfs-data \
    "
    MENDER_FEATURES_DISABLE:append = " mender-image mender-part-images"
    PREFERRED_VERSION_mender-artifact-native = "4.%"

target:
  - mender-explicit-demo-image

```

The `meta-mender-explicit-wic` layer is included as a sublayer of [meta-mender-community](https://github.com/mendersoftware/meta-mender-community). The configuration explicitly disables Mender’s dynamic image features and sets up the Raspberry Pi for U-Boot booting. The `mender-uboot` feature and `RPI_USE_U_BOOT` settings are specific to this U-Boot-based example; other platforms would use their appropriate bootloader integration.

## Customization

### Porting to Other Machines

To support a different machine:

1. Create a new WKS file named `mender-explicit-${MACHINE}.wks` in the `wic/` directory

2. Adjust the `--ondisk` parameter:

3. Choose the partition table type:

4. Adjust the boot partition source if needed:

5. Match the bootloader integration:

### Adjusting Partition Sizes

Root partition sizes are controlled by `--fixed-size`. Both Root A and Root B must use the same value:

```wks
part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --label rootA --align 8192 --fixed-size 1024M
part --ondisk mmcblk0 --fstype=ext4 --label rootB --align 8192 --fixed-size 1024M

```

The data partition uses `--size` (minimum size) rather than `--fixed-size`, allowing it to expand with `mender-growfs-data` on first boot:

```wks
part /data --source rootfs --change-directory=data --ondisk mmcblk0 --fstype=ext4 --label data --align 8192 --size 256

```

### Adding Custom Partitions

You can add additional partitions. Insert them after the data partition to avoid breaking Mender’s expectations:

```wks
# Recovery partition
part /recovery --source rawcopy --sourceparams="file=recovery.img" --ondisk mmcblk0 --fstype=ext4 --label recovery --align 8192 --size 256

```

Ensure any custom partitions don’t interfere with Mender’s partition numbering expectations. Mender typically expects:

- Partition 1: Boot
- Partition 2: Root A
- Partition 3: Root B
- Partition 4: Data

Additional partitions should come after these.

## Building

Standard build:

```bash
kas build kas/demos/raspberrypi4-64-mender-explicit-wic.yml

```

To use the dual-root variant for testing:

```bash
WKS_FILE=mender-explicit-raspberrypi4-64-dual-root.wks kas build kas/demos/raspberrypi4-64-mender-explicit-wic.yml

```

## Build Outputs

After a successful build, find these files in `tmp/deploy/images/raspberrypi4-64/`:

- `mender-explicit-demo-image-raspberrypi4-64.wic` - Full disk image for SD card flashing
- `mender-explicit-demo-image-raspberrypi4-64.wic.bz2` - Compressed disk image
- `mender-explicit-demo-image-raspberrypi4-64.mender` - OTA artifact for Mender server deployment

Flash the WIC image to an SD card:

```bash
bzcat mender-explicit-demo-image-raspberrypi4-64.wic.bz2 | sudo dd of=/dev/sdX bs=4M status=progress

```

## Verification

After booting, verify Mender is functional:

```bash
mender show-artifact

```

This should display the artifact name set in the configuration (`explicit-wic`).

To verify the partition layout:

```bash
lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT

```

You should see the expected partition structure with labels `boot`, `rootA`, `rootB`, and `data`.

## Summary

This approach gives you complete visibility and control over the Mender partition layout while maintaining full A/B update compatibility. The WKS file serves as explicit documentation of your disk layout, making it easier to understand, modify, and debug compared to the dynamic generation approach.

The trade-off is additional maintenance: you’re now responsible for keeping the WKS file, bbclass, and image recipe in sync with Mender’s requirements. For most production deployments, the standard Mender integration is simpler. But when you need that extra control, explicit WKS partitioning delivers it.

## Resources

### Demo Source Code

- [meta-mender-explicit-wic layer](https://github.com/mendersoftware/meta-mender-community/tree/scarthgap/kas/demos/meta-mender-explicit-wic) - Complete demo implementation
- [KAS configuration file](https://github.com/mendersoftware/meta-mender-community/blob/scarthgap/kas/demos/raspberrypi4-64-mender-explicit-wic.yml) - Build configuration

### Mender Documentation

- [Bootloader support overview](https://docs.mender.io/operating-system-updates-yocto-project/board-integration/bootloader-support)
- [U-Boot integration](https://docs.mender.io/operating-system-updates-yocto-project/board-integration/bootloader-support/u-boot)
- [GRUB integration](https://docs.mender.io/operating-system-updates-yocto-project/board-integration/bootloader-support/grub)
- [Creating Mender Artifacts](https://docs.mender.io/artifact-creation/create-an-artifact)

### Yocto Documentation

- [OpenEmbedded Kickstart (.wks) Reference](https://docs.yoctoproject.org/ref-manual/kickstart.html)
- [Creating Partitioned Images Using Wic](https://docs.yoctoproject.org/dev/dev-manual/wic.html)

### Repositories

- [meta-mender](https://github.com/mendersoftware/meta-mender) - Core Mender Yocto layer
- [meta-mender-community](https://github.com/mendersoftware/meta-mender-community) - Community board integrations
- [KAS](https://github.com/siemens/kas) - Yocto build setup tool
