Building a kernel snap

The kernel snap is one of Ubuntu Core’s key components. It holds the Linux kernel image and its associated modules, the ramdisk image for system initialisation, and optional firmware and device tree files.

It’s one of the essential snaps that need to be specified in the model assertion when building a custom image. Being such a vital part of the system, it can be updated but it cannot be swapped.

See Types of snap for details on which snaps are integral to Ubuntu Core’s functionality.

Canonical publishes several reference kernel snaps, alongside kernel snaps for models such as the official Ubuntu Core VMs on various certified public clouds. There are also several general purpose computing images for popular physical devices such as 64-bit x86 PC and Raspberry Pi 2, 3 and 4:

A custom build of the Linux kernel allows for device-specific architectures, configuration and modifications, and manually building the kernel snap has the same advantages and requirements. Consequently, building a working kernel first is highly recommended, before migrating this to a kernel snap build. The same configuration options and dependencies will be required.

The main difference between a standard manual kernel build and building a kernel snap is that the latter is built via the snapcraft command, and its accompanying snapcraft.yaml, as outlined below.


Using LXD

Unlike broader snap packages, kernel snaps are typically built within the host environment using snapcraft --destructive-mode. This step can still be isolated from the host system by building the kernel with LXD. To do this, install LXD (if it’s not already installed), instantiate an Ubuntu image (Ubuntu 20.04.2.0 LTS Focal Fossa), and create the kernel build there:

$ sudo snap install lxd
$ sudo lxd init --auto
$ sudo lxc launch ubuntu:20.04 focal
$ sudo lxc shell focal
# snap install snapcraft --classic

kernel snapcraft.yaml

The Ubuntu Linux kernels include a snapcraft.yaml for building a kernel snap, as do the kernels in the sample-kernels repository.

The following is a breakdown of a typical snapcraft.yaml used to build a kernel snap. It will need to be modified to fit your chosen platform and requirements, starting with the standard global keys and values used by all snaps:

name: kernel-snap-name
version: kernel version
summary: kernel snap quick description 
description: kernel snap longer description
grade: stable
confinement: strict

Next comes the snap-type definition to specify that we’re building a kernel snap:

type: kernel

The influence of the host environment, or LXD instance, can be mitigated with the optional build-base key, used to explicitly define the base snap used for the build environment.

build-base: core18

Similarly, it’s also common to build a kernel for a different architecture. This can be accomplished with architectures. The following example allows for an arm64 kernel to be built from an amd64 host:

architectures:
  - build-on: amd64
    run-on: arm64

The kernel part declares the kernel plugin, its arguments, and the kernel build configuration:

parts:
 kernel:
    plugin: kernel
    source: .
    source-type: git
    kconfigflavour: generic
    kconfigs:
      - CONFIG_DEBUG_INFO=n
    kernel-image-target: Image
    kernel-with-firmware: false

The kernel plugin documentation lists the available options, but it’s the kconfigs: key that lists the kernel configuration options for the kernel snap.

The kernel can optionally be accompanied by a firmware section for bundling the firmware within the snap (the following example taken from the Ubutu 20.04 kernel):

 firmware:
    plugin: nil
    stage-packages:
      - linux-firmware
    organize:
      lib/firmware: firmware
    prime:
      - -usr
      - -lib
    build-packages:
      - cpio
      - libssl-dev

Building the kernel snap

With snapcraft.yaml complete, and the kernel source either cloned locally or linked to from the snapcraft.yaml, the snapcraft command will build the kernel. As mentioned earlier, it’s often more convenient to build the kernel within the host environment, using --destructive-mode:

# snapcraft --destructive-mode --target-arch=arm64
[...]
Snapped kernal-snap-name_arm64.snap

If the above command was executed within an LXD environment, the resultant kernel snap can be extracted with the following commands:

# exit
$ lxc file pull path/to/kernal-snap-name_arm64.snap .

The kernel snap has now been built and is ready to be integrated into an Ubuntu Core image. See Custom images for further details.

Last updated 3 months ago. Help improve this document in the forum.