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.
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:
- pi-kernel (the generic Raspberry Pi kernel for all supported Pi devices)
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.
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.4 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
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:
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.
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
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 Ubuntu 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
# 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.