Creating bootable images

This document describes the requirements to create standard container images that can be used for cOS deployments

A derivative is a simple container image which can be processed by the cOS toolkit in order to be bootable and installable. This section describes the requirements to create a container image that can be run by cOS.


Bootable images are standard container images, that means the usual build and push workflow applies, and building images is also a way to persist oem customizations.

The base image can be any Linux distribution that is compatible with our flavors.

The image needs to ship:

  • parts of the cos-toolkit (required, see below)
  • kernel (required)
  • initrd (required)
  • grub (required)
  • dracut (optional, kernel and initrd can be consumed from the cOS repositories)
  • microcode (optional, not required in order to boot, but recomended)
  • cosign and luet-cosign packages (optional, required if you want to verify the images installed by luet)


An illustrative example can be:


FROM opensuse/leap:15.3

ENV COSIGN_REPOSITORY=raccos/releases-green

ARG ARCH=amd64
RUN zypper in -y \
    bash-completion \
    conntrack-tools \
    coreutils \
    curl \
    device-mapper \
    dosfstools \
    dracut \
    e2fsprogs \
    findutils \
    gawk \
    gptfdisk \
    grub2-i386-pc \
    grub2-x86_64-efi \
    haveged \
    iproute2 \
    iptables \
    iputils \
    issue-generator \
    jq \
    kernel-default \
    kernel-firmware-bnx2 \
    kernel-firmware-i915 \
    kernel-firmware-intel \
    kernel-firmware-iwlwifi \
    kernel-firmware-mellanox \
    kernel-firmware-network \
    kernel-firmware-platform \
    kernel-firmware-realtek \
    less \
    lsscsi \
    lvm2 \
    mdadm \
    multipath-tools \
    nano \
    nfs-utils \
    open-iscsi \
    open-vm-tools \
    parted \
    pigz \
    policycoreutils \
    procps \
    python-azure-agent \
    qemu-guest-agent \
    rng-tools \
    rsync \
    squashfs \
    strace \
    systemd \
    systemd-sysvinit \
    tar \
    timezone \
    vim \

RUN zypper cc

# Configure NetworkManager as default network management service
RUN zypper remove -y wicked
RUN systemctl disable wicked \
    && systemctl enable NetworkManager

# Copy the luet config file pointing to the upgrade repository
COPY conf/luet.yaml /etc/luet/luet.yaml

# Copy luet from the official images
COPY --from=luet /usr/bin/luet /usr/bin/luet

RUN luet install -y meta/cos-verify

RUN luet install --plugin luet-cosign -y meta/cos-minimal \
    utils/k9s \

COPY files/ /
RUN mkinitrd
Complete source code:

With the config file:

  color: false
  enable_emoji: false
   debug: false
   spinner_charset: 9
- name: "cos"
  description: "cOS official"
  type: "docker"
  enable: true
  cached: true
  priority: 1
  verify: false
  - ""
Complete source code:

In the example above, the cos-toolkit parts that are required are pulled in by RUN luet install -y meta/cos-minimal. Afterwards we install k9s and nerdctl packages to create our derivative with those packages on it.

meta/cos-minimal is a meta-package that will pull toolchain/luet , toolchain/yip , utils/installer , system/cos-setup , system/immutable-rootfs , system/base-dracut-modules , system/grub2-config , system/cloud-config .

Using cosign in your derivative

The meta/cos-verify is a meta package that will pull toolchain/cosign and toolchain/luet-cosign .

toolchain/cosign and toolchain/luet-cosign are optional packages that would install cosign and luet-cosign in order to verify the packages installed by luet.

You can use cosign to both verify that packages coming from cos-toolkit are verified and sign your own derivative artifacts

For more info, check the cosign page.


The image should provide at least grub, systemd, dracut, a kernel and an initrd. Those are the common set of packages between derivatives. See also package stack. By default the initrd is expected to be symlinked to /boot/initrd and the kernel to /boot/vmlinuz, otherwise you can specify a custom path while building an iso and by customizing grub.

system/base-dracut-modules is required to be installed with luet in case you are building manually the initrd from the Dockerfile and also to run dracut to build the initrd, the command might vary depending on the base distro which was chosen.

system/kernel and system/dracut-initrd can also be installed if you plan to use kernels and initrd from the cOS repositories and don’t build them / or install them from the official distro repositories (e.g. with zypper, or dnf or either apt-get…). In this case you don’t need to generate initrd on your own, neither install the kernel coming from the base image.


The workflow would be then:

  1. docker build the image
  2. docker push the image to some registry
  3. elemental upgrade --no-verify --docker-image $IMAGE from a cOS machine or (elemental reset if bootstrapping a cloud image)

The following can be incorporated in any standard gitops workflow.

You can explore more examples in the example section on how to create bootable images.

What’s next?

Now that we have created our derivative container, we can either:

Last modified May 6, 2022 : Skip generating docs (d29a239)