Live layering

Live layering allows to activates/deactivates system extension images. System extension images may – dynamically at runtime — extend the /usr/ and /opt/ directory hierarchies with additional files.

Kairos supports live layering with systemd-sysext. Currently it is supported only on the openSUSE, Fedora and Ubuntu flavors with systemd-sysext.


For general reference on how systemd-sysext works, please read the official documentation.

Systemd system extensions are searched for in the directories /etc/extensions/, /run/extensions/ and /var/lib/extensions/.

In order to install extensions in runtime, they need to be placed into /var/lib/extensions which is mounted over the COS_PERSISTENT partition. The other paths are reserved for the system image, which could ship extension directly from the container image used for upgrade or deployment.

Installing extensions

In order to install extensions, you can just place them into /var/lib/extensions.

For example, on a running Kairos node to install an extension from a container image:

luet util unpack <image> /var/lib/extensions/<extension_name>

To load an extension during installation of a Kairos node, it can be supplied as a bundle in the install block in the node configuration:


# Set username and password
     - name: "Set user and password"
          passwd: "kairos"
       hostname: kairos-{{ trunc 4 .Random }}
# Install configuration block
  auto: true
  reboot: true
  device: auto
  # Bundles to install
  - rootfs_path: /var/lib/extensions/<name>
    - container://<image>

Building extensions

Systemd extensions can be images, directory or files, quoting the systemd-sysext documentation:

  • Plain directories or btrfs subvolumes containing the OS tree

  • Disk images with a GPT disk label, following the Discoverable Partitions Specification

  • Disk images lacking a partition table, with a naked Linux file system (e.g. squashfs or ext4)

All of those can be shipped as a container image and loaded as a bundle.

For example, a bundle can be defined as a naked container image containing only the files that we want to overlay in the system.

Consider the following Dockerfile to create an extension which adds /usr/bin/ipfs to the system:

FROM alpine as build

# Install a binary
RUN wget -O kubo.tar.gz
RUN tar xvf kubo.tar.gz
RUN mv kubo/ipfs /usr/bin/ipfs
RUN mkdir -p /usr/lib/extension-release.d/
RUN echo ID=_any > /usr/lib/extension-release.d/extension-release.kubo

FROM scratch

COPY --from=build /usr/bin/ipfs /usr/bin/ipfs
COPY --from=build /usr/lib/extension-release.d /usr/lib/extension-release.d

Last modified February 23, 2024: Reduce sizes and remove warnings (0e183ae)