"I want to run my own custom Linux on a Raspberry Pi."
"I don't want Raspbian — I want to build the OS entirely from scratch."
"I'm prototyping an IoT product on Raspberry Pi and need a stripped-down, controlled OS."
If any of these describes you, Yocto Project is exactly what you need. This guide walks you through building a custom Linux image for Raspberry Pi using Yocto — from downloading the source layers all the way to booting on real hardware.
What You'll Need
- Build machine: Ubuntu 22.04/24.04, 16+ GB RAM, 200+ GB disk
- Raspberry Pi 4 or 5
- microSD card: 16 GB+ (Class 10 recommended)
- SD card reader
- HDMI monitor + keyboard (for initial boot verification)
Prerequisites
- You understand Yocto fundamentals (BitBake, layers, recipes)
- You've successfully built
core-image-minimalwith Poky - You're comfortable with basic Linux command-line operations
New to Yocto? Read the Yocto Beginner's Guide first.
What You'll Learn
- How to set up the meta-raspberrypi layer
- How to configure Raspberry Pi-specific build settings
- How to write the image to an SD card
- How to boot and verify on real hardware
- How to enable Wi-Fi, SSH, and GPIO
The Overall Workflow
- Download Poky and the required layers
- Add layers to bblayers.conf
- Set MACHINE in local.conf
- Build with bitbake
- Write the image to an SD card
- Boot and verify on Raspberry Pi
Step 1: Download the Required Layers
# Create a working directory
$ mkdir -p ~/yocto && cd ~/yocto
# Download Poky (styhead = latest stable)
$ git clone -b styhead git://git.yoctoproject.org/poky.git
# Download meta-raspberrypi
$ git clone -b styhead git://git.yoctoproject.org/meta-raspberrypi
# Download meta-openembedded (required by meta-raspberrypi)
$ git clone -b styhead git://git.openembedded.org/meta-openembedded
# Verify the directory layout
$ ls
meta-openembedded meta-raspberrypi poky
Use the same branch (styhead) for all three repositories. Mixing branches is a common source of mysterious compatibility errors.
Step 2: Initialize the Build Environment
$ cd ~/yocto/poky
$ source oe-init-build-env build-rpi
# BitBake automatically moves you to the build-rpi/ directory
# conf/local.conf and conf/bblayers.conf are auto-generated on first run
Naming the build directory build-rpi keeps it separate from any other build targets you might have (like a QEMU build).
Step 3: Add the Layers
# Add meta-oe (dependency required by meta-raspberrypi)
$ bitbake-layers add-layer ../../meta-openembedded/meta-oe
# Add meta-python (optional — needed if you're using Python packages)
$ bitbake-layers add-layer ../../meta-openembedded/meta-python
# Add meta-networking (optional — needed for networking tools)
$ bitbake-layers add-layer ../../meta-openembedded/meta-networking
# Add meta-raspberrypi
$ bitbake-layers add-layer ../../meta-raspberrypi
# Verify all layers are active
$ bitbake-layers show-layers
layer path priority
================================================================================
meta /home/user/yocto/poky/meta 5
meta-poky /home/user/yocto/poky/meta-poky 5
meta-yocto-bsp /home/user/yocto/poky/meta-yocto-bsp 5
meta-oe /home/user/yocto/meta-openembedded/meta-oe 6
meta-raspberrypi /home/user/yocto/meta-raspberrypi 9
Step 4: Configure local.conf
Edit conf/local.conf with Raspberry Pi-specific settings:
$ nano conf/local.conf
Add or modify the following:
# ========================================
# Raspberry Pi build configuration
# ========================================
# Target board
# raspberrypi4-64 : Raspberry Pi 4 (64-bit)
# raspberrypi5 : Raspberry Pi 5
# raspberrypi3-64 : Raspberry Pi 3 (64-bit)
MACHINE = "raspberrypi4-64"
# Parallel build settings (tune to your CPU core count)
BB_NUMBER_THREADS = "8"
PARALLEL_MAKE = "-j 8"
# Generate SD card image in wic format
IMAGE_FSTYPES = "wic.bz2 wic.bmap"
# GPU memory allocation (MB)
GPU_MEM = "64"
# Enable UART for serial console debugging
ENABLE_UART = "1"
# Enable SSH server (Dropbear)
IMAGE_FEATURES += "ssh-server-dropbear"
# Allow root login without a password (development only — disable for production)
EXTRA_IMAGE_FEATURES += "debug-tweaks"
# Use U-Boot bootloader
RPI_USE_U_BOOT = "1"
Supported MACHINE Values
| MACHINE | Target Board | Architecture |
|---|---|---|
raspberrypi5 | Raspberry Pi 5 | 64-bit (aarch64) |
raspberrypi4-64 | Raspberry Pi 4 | 64-bit (aarch64) |
raspberrypi4 | Raspberry Pi 4 | 32-bit (armhf) |
raspberrypi3-64 | Raspberry Pi 3 | 64-bit (aarch64) |
raspberrypi3 | Raspberry Pi 3 | 32-bit (armhf) |
raspberrypi2 | Raspberry Pi 2 | 32-bit (armhf) |
raspberrypi0-2w-64 | Raspberry Pi Zero 2 W | 64-bit (aarch64) |
I recommend 64-bit targets for Pi 3 and newer. Memory management is better and 64-bit is where the ecosystem is heading.
Step 5: Build the Image
# Build a minimal image (smallest footprint)
$ bitbake core-image-minimal
# Or build a base image with slightly more tooling
$ bitbake core-image-base
# Expected build times (first run):
# - 8 cores / 32 GB RAM: ~1.5–2 hours
# - 4 cores / 16 GB RAM: ~3–4 hours
Verifying the Build Output
# List the generated files
$ ls tmp/deploy/images/raspberrypi4-64/
core-image-minimal-raspberrypi4-64.wic.bz2 # Compressed SD card image
core-image-minimal-raspberrypi4-64.wic.bmap # Block map for fast flashing
Image # Kernel image
bcm2711-rpi-4-b.dtb # Device tree blob
Step 6: Flash the SD Card
Method 1: bmaptool (Recommended — Much Faster)
bmaptool uses the .bmap file to skip empty blocks during writing. It's significantly faster than dd for sparse images.
# Install bmaptool
$ sudo apt install bmap-tools
# Check which device is your SD card
$ lsblk
# Flash the image (replace /dev/sdX with your actual device)
$ cd tmp/deploy/images/raspberrypi4-64/
$ sudo bmaptool copy core-image-minimal-raspberrypi4-64.wic.bz2 /dev/sdX
# Sync to ensure all writes complete
$ sync
Method 2: dd (Traditional)
# Decompress and write in one command
$ bzcat core-image-minimal-raspberrypi4-64.wic.bz2 | sudo dd of=/dev/sdX bs=4M status=progress
$ sync
Warning: Always double-check the /dev/sdX device name with lsblk before running dd. Writing to the wrong device will overwrite your host system's hard drive. There is no undo.
Step 7: First Boot on Raspberry Pi
- Insert the SD card into your Raspberry Pi
- Connect an HDMI monitor and keyboard
- Connect power and let it boot
- When the login prompt appears, log in as
root(no password)
Poky (Yocto Project Reference Distro) 5.1 raspberrypi4-64 ttyAMA0
raspberrypi4-64 login: root
root@raspberrypi4-64:~# uname -a
Linux raspberrypi4-64 6.6.x-yocto-standard #1 SMP ... aarch64 GNU/Linux
root@raspberrypi4-64:~# cat /etc/os-release
NAME="Poky (Yocto Project Reference Distro)"
VERSION="5.1 (styhead)"
You're running a fully custom Linux built from source. Everything in that image — every binary, every library — was cross-compiled specifically for this hardware by your build machine.
Additional Configuration: Wi-Fi, SSH, GPIO
Enabling Wi-Fi
Add to conf/local.conf:
IMAGE_INSTALL:append = " linux-firmware-bcm43455 wpa-supplicant"
You can pre-configure Wi-Fi credentials with a wpa_supplicant.conf file included via a .bbappend on the wpa-supplicant recipe.
SSH Remote Login
Since you already added ssh-server-dropbear to IMAGE_FEATURES, SSH is ready to go.
# Check the IP address (with wired connection)
root@raspberrypi4-64:~# ip addr show eth0
# Connect from your development machine
$ ssh root@192.168.1.xxx
In practice, I use SSH for all day-to-day work after the first boot. The HDMI monitor is just for verifying the initial boot.
GPIO Control
# Add GPIO tools to local.conf
IMAGE_INSTALL:append = " libgpiod libgpiod-tools"
After rebuilding and reflashing:
root@raspberrypi4-64:~# gpioinfo
root@raspberrypi4-64:~# gpioset gpiochip0 17=1 # Set GPIO17 HIGH
root@raspberrypi4-64:~# gpioget gpiochip0 27 # Read GPIO27
Troubleshooting
Black screen — nothing on HDMI
Cause: HDMI output configuration doesn't match your monitor.
Fix: Add to conf/local.conf:
HDMI_FORCE_HOTPLUG = "1"
HDMI_GROUP = "2"
HDMI_MODE = "82" # 1080p 60Hz
Kernel panic — not syncing
Cause: Mismatch between kernel and root filesystem.
Fix: Rebuild the image cleanly and re-flash the SD card. If the card seems flaky, try formatting it first with a tool like fdisk or just use a different card.
wic.bz2 image not found
ERROR: No such file or directory: ...wic.bz2
Cause: IMAGE_FSTYPES doesn't include wic.
Fix: Add this to conf/local.conf:
IMAGE_FSTYPES = "wic.bz2 wic.bmap"
INCOMPATIBLE_LICENSE error
ERROR: xxx has incompatible license
Cause: A package with commercial licensing requirements was included.
Fix: Explicitly accept the license in local.conf:
LICENSE_FLAGS_ACCEPTED = "commercial"
Summary
Here's what we covered:
- meta-raspberrypi provides all the Raspberry Pi board support
- MACHINE setting selects the exact target board
- wic format creates a ready-to-flash SD card image
- bmaptool is the fastest way to write images to SD cards
You now have a completely custom Linux running on Raspberry Pi — no Raspbian, no pre-installed bloat, just exactly what you put in the image. Strip out everything you don't need, add your own applications, and you have the foundation for a production IoT device.
The same Yocto workflow scales from prototype to production. That's one of the big reasons Yocto is worth the upfront learning investment.
Next Steps
- Yocto Project Complete Beginner's Guide
- How to Write Custom Yocto Recipes
- Yocto Layers Explained: Creating and Adding meta-xxx
This article reflects the state of Yocto Project and meta-raspberrypi as of January 2026. Procedures may change as new versions are released.