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.
- 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.
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 | Ubuntu Core | |
|---|---|---|
| Build model | Compile everything from source via layers/recipes | Compose pre-built snaps |
| Update mechanism | Not included — bring your own (RAUC, SWUpdate, Mender, OSTree) | Built in — atomic, with automatic rollback |
| Flexibility | Total — any kernel, any rootfs, any package set | Constrained to what the snap packaging model supports |
| Learning curve | Steep — BitBake, layers, recipe syntax | Gentler — snapcraft.yaml, familiar packaging concepts |
| Support model | Community plus vendor BSPs; you own the integration | Commercial support available directly from Canonical |
| Best fit | Fully custom hardware, tight resource budgets, unusual requirements | Fast time-to-market, fleet management, common reference hardware |
Real-world applications
- Custom silicon bring-up — a new SoC with no off-the-shelf distro support typically starts from a vendor's
meta-<vendor>BSP layer as the Yocto starting point. - Resource-constrained devices — Yocto's recipe-level control lets a team strip a rootfs down to exactly what a small-flash device needs, byte by byte.
- Owning the update/security story — a team that can't or won't depend on a third-party store's update infrastructure builds and maintains its own OTA mechanism on top of a Yocto image.
- Fleet devices choosing Ubuntu Core instead — when atomic rollback and fast time-to-market matter more than absolute rootfs control, Ubuntu Core's built-in update story is often the better trade.
Gotchas
- A first full image build compiles essentially an entire Linux distribution from source — tens of gigabytes of disk and hours of build time are normal; a shared
sstate-cache/downloadsdirectory across projects turns that into minutes on repeat builds. - Two layers that both define the same recipe (or the same
.bbappendtarget) can silently resolve to the "wrong" one ifBBFILE_PRIORITYisn't set deliberately — Yocto won't necessarily warn you. - Yocto has no built-in OTA update mechanism at all — teams sometimes only discover this after board bring-up is well underway, needing to bolt on RAUC/SWUpdate/Mender/OSTree later rather than by design from day one.
- "Poky" and "Yocto" get used interchangeably in casual conversation but aren't the same thing — Poky is one specific reference distribution built with Yocto's tools, not a synonym for the project itself.