Introduction

Yocto makes it easy to build images you can flash straight to an SD card or eMMC. A image typically contains a small boot partition and a larger rootfs partition, both with sizes fixed in a config file. The catch? The final image size equals the sum of those partitions. If you want more room for user data, you have to ship a bigger image. However, this comes at a cost:

  • Bigger files – slower to transfer, more storage in artifact repos

  • Longer flashing time – slower production

  • Wasted capacity – the device might not use full flash size in the field

In this article, I’ll show a better approach: keep the image small (or even smaller than the original), then grow the /home (or data) partition automatically on first boot. You get fast flashing, small artifacts, and devices that expand to use 100% of the available storage, no manual steps required. As a bonus, you can make the rootfs partition read-only not to mess it up during usage.

The .wks file

You start off by creating a .wks file that will override the default one. A .wks file describes how the image file is built up by listing all the partitions and their properties.
Typically, the default .wks file is shown below. It specifies as 50MB boot partition and a 400MB roofts partition. The /home directory is part of the rootfs.

				
					# Description: Creates a partitioned SD card image. 
# Boot files are located in the first partition (vfat).
# Rootfs with /home is in the second partition (ext4)

part --source bootimg-partition --fstype=vfat --label boot --active --align 4 --fixed-size 50M
part --source rootfs --fstype=ext4 --label root --align 4 --fixed-size 400M
				
			

If you are not sure what .wks file is used, the following command should give you a clue:

				
					bitbake petalinux-image-minimal -e | grep "WKS_FILES"
				
			

In Yocto, there are two relevant variables, WKS_FILE and WKS_FILES. Given their default definitions in image_types_wic.bbclass it should be safe to check only WKS_FILES.

				
					WKS_FILE ??= "${IMAGE_BASENAME}.${MACHINE}.wks"
WKS_FILES ?= "${WKS_FILE} ${IMAGE_BASENAME}.wks"
				
			

You can use the current .wks file as a template. In the below example we have added a teeny-tiny 1MB partition for /home.
We have also reduced the rootfs partition by 100MB. Now as /home is moved to a separate partition we no longer need to accommodate for it in the rootfs partition. The result is an image that is actually 99MB smaller than the original.

				
					# Description: Creates a partitioned SD card image.
# Boot files are located in the first partition (vfat).
# Rootfs is in the second partition (ext4).
# Home partition is the third partition (ext4)
part --source bootimg-partition --fstype=vfat --label boot --active --align 4 --fixed-size 50M
part /     --source rootfs --rootfs-dir=${IMAGE_ROOTFS}      --fstype=ext4 --label root --align 4 --fixed-size 300M
part /home --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/home --fstype=ext4 --label home --align 4 --fixed-size 1M --ondisk mmcblk1

				
			

IMAGE_ROOTFS is the full path to the built root-filesystem directory of the image you’re building (something like build/tmp/work/<machine>/<image>/<ver>/rootfs). Yocto writes it into a wic environment file located in build/tmp/deploy/images/<machine>/<image>.env, so you can safely reference ${IMAGE_ROOTFS} inside a .wks and wic will expand it.

The 1MB size of the /home partition is just for show off. Normally you would have a sane minimal size in case the resizing fails.

Applying new .wks file

Now as we have a new .wks file, we will make Yocto use it.

There are two options how to do that based on where the partition layout policy should live. If the policy is tied up to the machine/hardware then it should be in the machine config file. Note that the disk for the /home partition is specified in the .wks file. This is not the only way, but the simplest. This means that if you need to build images where the /home partition is on different disks, you would probably prefer to make it a machine policy and have multiple .wks files.
If the policy is tied up to the image, regardless of the machine the image will be built for, it can be in the image recipe. In this example it is the latter.

If you have your custom image recipe then you can override WKS_FILES in that recipe. If you build a predefined image like core-image-minimal, you can override WKS_FILES in a .bbappend file.

				
					WKS_FILES:my-machine = "my_new_wks_file.wks" 
				
			

The my_new_wks_file.wks file can be placed in a wic directory. It does not have to be related with any recipes. Yocto will find it.

Growing your /home

We will grow the /home partition on first boot. The idea is to fire up a systemd service before /home is mounted, and resize it to fill up all available space on the SD-card (or eMMC).

grow-home.service

The service is super simple, the magic is in the script that the service executes. We will come to that in a second. Note that we need to run this service Before=home.mount and After=dev-disk-by\\x2dpartlabel-home.device, meaning when /home is not yet mounted but the partition has been recognized. The fancy \\x2d is not a mistake, it is just escape of "-" as in /dev/disk/by-label/home.

				
					[Unit]
# This service should run only once at the very first boot
# The script is responsible for disabling the service
Description=Resize /home partition on first boot
Before=home.mount
After=dev-disk-by\\x2dlabel-home.device
DefaultDependencies=no

[Service]
Type=oneshot
ExecStart=/sbin/grow-home.sh
RemainAfterExit=yes

[Install]
WantedBy=home.mount
				
			
grow-home.sh

All the resizing action is done in this script. The script identifies the /home partition in a generic fashion. Note that the service is disabled at the end so it doesn’t run on consecutive boots.

				
					#!/usr/bin/env bash
set -e

PART=$(blkid -t LABEL=home -o device)
DISK="${PART%%p[0-9]*}"
PARTNUM="${PART##*[!0-9]}"
echo "Resizing $PART..."

# Resize the partition
# This uses parted to resize the partition to the maximum size
parted $DISK --script resizepart $PARTNUM 100%

# Re-read partition table
partprobe $DISK
# Resize the filesystem
e2fsck -y -f "$PART"
resize2fs "$PART"

# Disable service so it doesn’t run again
systemctl disable grow-home.service
echo "Resize complete."

				
			
grow-service.bb

Both the service and the script is installed in the Yocto image by a simple recipe.

				
					DESCRIPTION = "Grow home partition service"
LICENSE = "CLOSED"
SRC_URI = "file://grow-home.service \
           file://grow-home.sh \
"
S = "${WORKDIR}"
RDEPENDS:${PN} += "bash parted e2fsprogs-resize2fs util-linux-blkid"
do_install() {
    install -m 0755 -d ${D}${base_sbindir}/
    install -m 0755 -d ${D}${systemd_system_unitdir}/
    install -m 0755 ${WORKDIR}/grow-home.sh ${D}${base_sbindir}/
    install -m 0644 ${WORKDIR}/grow-home.service ${D}${systemd_system_unitdir}/
}
FILES:${PN} = " \
    ${systemd_system_unitdir}/grow-home.service \
    ${base_sbindir}/grow-home.sh \
"
inherit systemd
SYSTEMD_SERVICE:${PN} = "grow-home.service"
				
			

The final recipe file structure should look like this:

Remember to add the recipe to your image.

Proof of the pudding

Build, flash and boot. The first boot should take a little bit longer, but not much. Now you can check if the /home partition has grown.

				
					root@refdes-xu8-st1-xczu4cg:~# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               280.5M    171.8M     89.7M  66% /
devtmpfs                820.0M      4.0K    819.9M   0% /dev
tmpfs                   949.4M         0    949.4M   0% /dev/shm
tmpfs                   379.8M      9.7M    370.1M   3% /run
tmpfs                   949.4M      4.0K    949.4M   0% /tmp
tmpfs                   949.4M      8.0K    949.4M   0% /var/volatile
/dev/mmcblk1p3           29.3G     17.0K     27.8G   0% /home
/dev/mmcblk1p1           49.9M     33.0M     16.9M  66% /run/media/boot-mmcblk1p1
tmpfs                   189.9M      4.0K    189.9M   0% /run/user/0
				
			

And indeed it has. It is now 27.8GB. Great success!

Conclusion

This configuration addresses a frequent challenge encountered by devices operating Embedded Linux: the need to deploy compact, quick-to-flash images that nevertheless utilize the device’s full storage capacity once deployed in the field. By implementing a dedicated /home (or data) partition that automatically expands during the initial boot process, this method separates the concept of image size from device size. The advantages include reduced build artifact sizes, accelerated update processes, and optimized flash memory usage without wastage.

The integration with Yocto is streamlined and modular: a concise .wks modification specifies the partition layout, a lightweight systemd service executes once to expand the partition, and a brief script performs the resizing operation securely. Following this initial boot, the system operates as a standard installation, with the entirety of the available storage allocated for user data.

About the author

With a background in Embedded Systems development, Michal has worked on projects ranging from bare-metal applications to Embedded Linux platforms. In his role as a Principal Software Developer and System Architect, he has taken on responsibility for delivering robust technical solutions in cross-disciplinary teams. He is particularly focused on systems engineering and requirements handling, and is known for a detail-oriented and proactive approach to development.