Linux

Yocto

Most embedded Linux devices don't run a stock desktop distro cut down to size — they run something built specifically for that board, that exact kernel config, and that exact package set. The Yocto Project is the dominant framework for building exactly that: not a Linux distribution itself, but a system for building your own.

Quick reference
What it is
A build framework (BitBake + layers of recipes) for compiling a fully custom embedded Linux distribution from source.
Mental model
Not a distro — a recipe book and a chef (BitBake) that turns layered recipes into a bootable image, entirely under your control.
Key numbers
A first full image build compiles everything from source — expect real disk space (tens of GB) and real time (hours), dramatically shortened on repeat builds by the shared cache.
Reach for it when
You need full control over the kernel, rootfs, and package set — custom silicon, tight resource budgets, or requirements a pre-built distro can't meet.

What Yocto actually is

The name causes real confusion: Yocto is the project and the tooling, not a distribution you install. Poky is Yocto's own reference distribution — a minimal, working example built with the tools, meant as a starting point rather than a finished product. What actually does the building is BitBake, a task-scheduling build tool, driven by the OpenEmbedded build system's metadata format — together they compile a Linux kernel, bootloader, C library, and every package your image needs, entirely from source, for whatever target architecture you specify.

Layers and recipes

A recipe (a .bb file) describes how to fetch, patch, configure, compile, and package one piece of software — a library, a kernel, an application. Recipes are grouped into layers (directories conventionally named meta-*) so that unrelated concerns — a vendor's BSP, your own application recipes, a middleware stack — stay in separate, independently versioned collections instead of one monolithic tree.

# meta-mylayer/recipes-example/hello/hello_1.0.bb
SUMMARY = "A simple hello-world example recipe"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://COPYING;md5=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

SRC_URI = "file://hello.c"
S = "${WORKDIR}"

do_compile() {
    ${CC} ${CFLAGS} hello.c -o hello
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 hello ${D}${bindir}
}

A machine configuration (conf/machine/*.conf, usually shipped in a vendor's BSP layer) declares board-specific settings — CPU architecture, kernel provider, boot files — and a distro configuration declares distribution-wide policy (init system, package format, feature set). Layer priority (BBFILE_PRIORITY) decides which layer wins when two layers both touch the same recipe.

BitBake and the build pipeline

BitBake parses every recipe visible across your configured layers, resolves dependencies between them, and executes each recipe's tasks in order — do_fetch, do_patch, do_configure, do_compile, do_install, do_package — before finally assembling everything into a root filesystem with do_rootfs.

BitBake parses layers and recipes, builds a task dependency graph, and produces a cross-compile toolchain, a root filesystem, and a kernel plus device tree from one command Layers & recipes meta-*, .bb, .bbclass, machine.conf BitBake resolves deps, runs the task graph Cross-compile toolchain for the SDK, if built Root filesystem the image's rootfs, packaged Kernel + device tree bootable kernel image, .dtb One command — bitbake core-image-minimal — produces all three outputs together.
Task outputs are cached (sstate-cache) so a later build only re-runs the tasks whose inputs actually changed — the difference between a from-scratch build and an incremental one.

Building an image

git clone git://git.yoctoproject.org/poky
cd poky
source oe-init-build-env

# add a vendor BSP layer and your own layer
bitbake-layers add-layer ../meta-raspberrypi
bitbake-layers add-layer ../meta-mylayer

# select the target board in conf/local.conf
echo 'MACHINE = "raspberrypi4-64"' >> conf/local.conf

# build — this is the single command that runs the whole pipeline above
bitbake core-image-minimal

The build produces a kernel image, a device tree blob (the same .dtb covered in Device Tree), and a root filesystem image — ready to flash onto the target board's storage.

Customizing without forking

A .bbappend file extends an existing recipe — adding a patch, changing a config option — without editing (or forking) the original recipe at all, which matters enormously when the original recipe lives in a layer you don't maintain.

# meta-mylayer/recipes-kernel/linux/linux-yocto_%.bbappend
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append = " file://0001-enable-my-sensor-driver.patch"

This is exactly the git am-style patch mechanics from Kernel Source, Versions & Patching, just declared in a recipe so it's applied automatically, in order, on every build instead of run by hand. Adding packages to an image is just as declarative:

# in your image recipe or conf/local.conf
IMAGE_INSTALL:append = " htop mosquitto python3"

Yocto vs. Ubuntu Core

Both build a custom embedded Linux image, but from opposite directions: Yocto compiles everything from source under your full control; Ubuntu Core composes a device image from pre-built, atomically-updatable snaps.

Yocto builds a custom image by compiling every component from source; Ubuntu Core assembles an image by composing pre-built gadget, kernel, base, and snapd snaps — both converge on a bootable device image Yocto compiles every component from source full control over kernel, rootfs, packages you own the update mechanism Ubuntu Core composes pre-built gadget/kernel/base/snapd snaps atomic update and rollback built in Canonical maintains the base layers Bootable device image
Same destination, opposite philosophies — Yocto's is "build it yourself, own every piece"; Ubuntu Core's is "assemble it from parts someone else maintains and updates."
YoctoUbuntu Core
Build modelCompile everything from source via layers/recipesCompose pre-built snaps
Update mechanismNot included — bring your own (RAUC, SWUpdate, Mender, OSTree)Built in — atomic, with automatic rollback
FlexibilityTotal — any kernel, any rootfs, any package setConstrained to what the snap packaging model supports
Learning curveSteep — BitBake, layers, recipe syntaxGentler — snapcraft.yaml, familiar packaging concepts
Support modelCommunity plus vendor BSPs; you own the integrationCommercial support available directly from Canonical
Best fitFully custom hardware, tight resource budgets, unusual requirementsFast time-to-market, fleet management, common reference hardware

Real-world applications

Gotchas

Field notes