A Yocto recipe is a .bb file that tells BitBake where to fetch source code, how to build it, and where to install it — the core mechanism for adding any software to your embedded Linux image.
This guide walks you through creating Yocto recipes from the ground up, with working code examples at every step. By the end, you'll be comfortable writing recipes for local source files, GitHub projects, and CMake/Autotools builds.
What Is a Yocto Recipe?
A recipe is a .bb file that describes how to build and install a specific piece of software. The Yocto Development Tasks Manual covers the full specification, but at its core, a recipe answers four questions:
- Where to get the source code (
SRC_URI) - How to build it (
do_compile) - Where to install it (
do_install) - What license it uses (
LICENSE)
Recipe File Naming
Recipe filenames follow the pattern packagename_version.bb:
hello-world_1.0.bb # hello-world version 1.0
myapp_2.3.1.bb # myapp version 2.3.1
busybox_1.36.1.bb # busybox version 1.36.1
BitBake automatically extracts the package name (PN) and version (PV) from the filename. See the Yocto Reference Manual — Variables Glossary for the full list of built-in variables.
Basic Recipe Structure
# hello-world_1.0.bb
# ===== Metadata =====
SUMMARY = "Hello World sample"
DESCRIPTION = "A minimal example recipe for learning Yocto"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
# ===== Source acquisition =====
SRC_URI = "file://hello.c"
# ===== Source directory =====
S = "${WORKDIR}"
# ===== Build =====
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -o hello hello.c
}
# ===== Install =====
do_install() {
install -d ${D}${bindir}
install -m 0755 hello ${D}${bindir}
}
Key Variables
Package Metadata
| Variable | Description | Example |
|---|---|---|
SUMMARY | Short one-line description | "Hello World sample" |
DESCRIPTION | Detailed description | "A minimal learning example..." |
LICENSE | License identifier (SPDX) | "MIT", "GPL-2.0-only", "Apache-2.0" |
LIC_FILES_CHKSUM | License file checksum | file://LICENSE;md5=xxx |
SRC_URI | Where to get the source | "file://", "git://", "https://" |
S | Where source is unpacked | $/git |
Build and Install Variables
| Variable | Description | Expands to |
|---|---|---|
${WORKDIR} | Working directory for this build | /tmp/work/.../hello-world/1.0-r0/ |
${D} | Staging root for installation | .../image/ |
${bindir} | /usr/bin path | /usr/bin |
${libdir} | /usr/lib path | /usr/lib |
${sysconfdir} | /etc path | /etc |
${CC} | Cross-compiler | arm-poky-linux-gnueabi-gcc |
Building a Hello World Recipe
Let's build the simplest possible recipe and confirm it works end-to-end.
Create a Custom Layer
Always put your recipes in your own layer — never in Poky's layers directly.
cd ~/yocto/poky
source oe-init-build-env build
# Create a new layer
cd ..
bitbake-layers create-layer meta-mylayer
# Add it to the build
cd build
bitbake-layers add-layer ../meta-mylayer
See the layer creation guide for details on layer management.
Create the Directory Structure and Source
mkdir -p ../meta-mylayer/recipes-example/hello-world/files
Write files/hello.c:
// files/hello.c
#include <stdio.h>
int main(void) {
printf("Hello from Yocto!\n");
printf("This app was built with a custom recipe.\n");
return 0;
}
Write the Recipe
Create hello-world_1.0.bb:
# hello-world_1.0.bb
SUMMARY = "Hello World - Yocto recipe learning example"
DESCRIPTION = "A minimal application demonstrating how to write a custom Yocto recipe"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://hello.c"
S = "${WORKDIR}"
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -o hello hello.c
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello ${D}${bindir}
}
Build and Include in Your Image
# Build the recipe individually
bitbake hello-world
# Success looks like:
NOTE: Tasks Summary: Attempted 123 tasks of which 120 didn't need to be rerun
Add to conf/local.conf to include it in the image:
IMAGE_INSTALL:append = " hello-world"
Rebuild and test in QEMU:
bitbake core-image-minimal
runqemu qemux86-64 nographic
# After boot:
root@qemux86-64:~# hello
Hello from Yocto!
This app was built with a custom recipe.
Fetching Source from GitHub
In real projects, you'll usually fetch source from an external repository rather than shipping it locally.
Example: Adding nlohmann/json
# nlohmann-json_3.11.3.bb
SUMMARY = "JSON for Modern C++"
DESCRIPTION = "A header-only JSON library for C++"
HOMEPAGE = "https://github.com/nlohmann/json"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE.MIT;md5=f969127d7b7ed0a8a63c2bbeae002588"
# Fetch from GitHub using HTTPS
SRC_URI = "git://github.com/nlohmann/json.git;protocol=https;branch=develop"
# Pin to a specific commit — always do this for reproducibility
SRCREV = "9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03"
S = "${WORKDIR}/git"
inherit cmake
EXTRA_OECMAKE = "-DJSON_BuildTests=OFF"
SRC_URI Patterns
| Source type | SRC_URI format |
|---|---|
| Local file | file://hello.c |
| Git (HTTPS) | git://github.com/user/repo.git;protocol=https;branch=main |
| Git (SSH) | git://git@github.com/user/repo.git;protocol=ssh;branch=main |
| HTTP/HTTPS tarball | https://example.com/file.tar.gz |
| Patch file | file://fix-build.patch |
Using Inherit Classes
Yocto ships with classes that encapsulate common build patterns. Using inherit saves you from writing boilerplate do_compile and do_install tasks.
| Class | Use Case | What It Automates |
|---|---|---|
cmake | CMake projects | cmake → make → make install |
autotools | Autoconf projects | configure → make → make install |
meson | Meson projects | meson → ninja → install |
setuptools3 | Python packages | setuptools / wheel install |
systemd | systemd services | Service enablement |
CMake Project Example
# myapp-cmake_1.0.bb
SUMMARY = "My CMake App"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "git://github.com/user/myapp.git;protocol=https;branch=main"
SRCREV = "abc123def456..."
S = "${WORKDIR}/git"
inherit cmake
EXTRA_OECMAKE = "-DBUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=Release"
Autotools Project Example
# myapp-autotools_1.0.bb
SUMMARY = "My Autotools App"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://COPYING;md5=..."
SRC_URI = "https://example.com/myapp-1.0.tar.gz"
SRC_URI[sha256sum] = "def456..."
inherit autotools
EXTRA_OECONF = "--disable-docs"
systemd Services
To automatically start your application at boot, use inherit systemd. For detailed configuration, see the systemd service guide.
Common Errors and Fixes
LIC_FILES_CHKSUM mismatch
ERROR: License checksum mismatch...
Cause: The MD5 hash in your recipe doesn't match the actual license file.
Fix: Recalculate the correct hash:
md5sum LICENSE
do_fetch failed
ERROR: Fetcher failure: Unable to find file...
Cause: Wrong path in SRC_URI, or network issue when fetching from Git.
Fix: Double-check your SRC_URI and SRCREV. Make sure the commit hash exists in the repository.
Nothing PROVIDES 'hello-world'
ERROR: Nothing PROVIDES 'hello-world'
Cause: The layer containing your recipe isn't in bblayers.conf.
Fix: Add the layer:
bitbake-layers add-layer ../meta-mylayer
SRC_URI checksum mismatch
ERROR: SRC_URI checksum mismatch...
Cause: Checksum for an HTTP/HTTPS download isn't set or doesn't match.
Fix: Calculate the correct checksum:
sha256sum downloaded-file.tar.gz
Managing Dependencies
Real-world recipes often depend on other packages. Yocto distinguishes between build-time and runtime dependencies.
DEPENDS vs RDEPENDS
| Variable | When it matters | Example |
|---|---|---|
DEPENDS | Build time — libraries/headers needed to compile | DEPENDS = "openssl zlib" |
RDEPENDS:${PN} | Runtime — packages needed on the target | RDEPENDS:${PN} = "libssl" |
# myapp_1.0.bb — with dependencies
SUMMARY = "My App with Dependencies"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://myapp.c"
S = "${WORKDIR}"
# Build-time: need OpenSSL headers and libraries to compile
DEPENDS = "openssl"
# Runtime: need OpenSSL shared libraries on the target
RDEPENDS:${PN} = "openssl"
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -o myapp myapp.c -lssl -lcrypto
}
do_install() {
install -d ${D}${bindir}
install -m 0755 myapp ${D}${bindir}
}
Using require and include
You can split recipe logic across multiple files:
require— includes another file; fails if not foundinclude— includes another file; silently skips if not found
# myapp_1.0.bb
require myapp-common.inc
SRC_URI = "https://example.com/myapp-1.0.tar.gz"
SRC_URI[sha256sum] = "abc123..."
This is commonly used in Poky's own recipes to share metadata across versions.
FAQ
What is the difference between SRCREV and AUTOREV?
SRCREV pins your recipe to a specific Git commit hash, ensuring reproducible builds across machines and CI environments. AUTOREV (set as SRCREV = "${AUTOREV}") fetches the latest commit on each build. The BitBake User Manual explicitly discourages AUTOREV for production use — it makes builds non-deterministic and can introduce untested changes.
How do I add multiple source files in SRC_URI?
List them separated by spaces or use line continuation with backslashes:
SRC_URI = "file://main.c \
file://utils.c \
file://utils.h \
file://config.json \
"
All file:// entries should be placed in a files/ directory next to the recipe, or in a directory matching the recipe name.
Can I write recipe logic in Python?
Yes. BitBake recipes support Python in two ways: inline with ${@expression} and as full Python functions with python do_taskname(). For example:
# Set a variable using Python
DATEVAR = "${@time.strftime('%Y%m%d', time.gmtime())}"
python do_configure:prepend() {
bb.note("Configuring with custom Python logic")
}
The BitBake User Manual covers Python tasks in detail.
What is the difference between inherit and require?
inherit loads a .bbclass file, which adds standardized build behavior (like cmake or autotools). require / include loads another .bb or .inc file to share recipe metadata. Think of inherit as "use this build system" and require as "import shared recipe settings."
How do I debug a recipe that fails to build?
Use devshell to drop into an interactive shell inside the recipe's build environment:
bitbake -c devshell hello-world
This opens a terminal with all environment variables (cross-compiler, sysroot paths) already set. You can manually run make, inspect files, and test fixes. See the build error debugging guide for a systematic approach to common failures.
How do I add configuration files to /etc?
Use ${sysconfdir} (which expands to /etc) in do_install:
do_install() {
install -d ${D}${sysconfdir}/myapp
install -m 0644 ${WORKDIR}/myapp.conf ${D}${sysconfdir}/myapp/
}
Don't forget to include the config file in SRC_URI as well.
What is the difference between do_install and do_deploy?
do_install puts files into the target rootfs staging area (${D}). do_deploy puts files into the deploy directory — used for boot images, bootloader binaries, or artifacts that aren't part of the rootfs but are needed for flashing. Most recipes only need do_install.
Wrapping Up
Here's what to remember about writing Yocto recipes:
- Recipes are
.bbfiles that describe a complete build process — fetch, build, install - Core variables:
SRC_URI,do_compile,do_install,DEPENDS,RDEPENDS - Always put your recipes in a custom layer, not in Poky
- Use
inheritto automatically handle CMake/Autotools/Meson projects - Add packages to an image with
IMAGE_INSTALL:append - Always pin
SRCREVto a specific commit hash for reproducibility - Use
DEPENDSfor build-time andRDEPENDSfor runtime dependencies
The Yocto Development Tasks Manual is the authoritative reference for recipe writing. Once you're comfortable with the basics, here's where to go next:
- Creating and managing layers — layer architecture for your recipes
- bbappend practical guide — customizing existing recipes
- Building Linux for Raspberry Pi — deploy to real hardware
- SBOM and CVE management — supply chain security for your recipes
To go deeper into embedded Linux, these books are excellent references.
Related articles:
- Yocto bbappend: A Practical Guide with 5 Patterns
- Yocto Layers: How to Create and Manage Custom Layers
- Getting Started with Yocto: Build Your First Linux Image
- Yocto Build Errors: Task-by-Task Debugging Guide
- Yocto systemd Auto-Start: Setup Guide with 7 Pitfalls
- 10x Faster Yocto Builds
- Raspberry Pi 5 with Yocto Scarthgap