How to Build and Run Yocto Images in Linux-Free Embedded Linux Course online

Build and Run Yocto Images
A free embedded Linux development course lecture — from BitBake build to QEMU boot
Free Linux Kernel Development Course
Free Embedded Systems Course
Free Linux Device Drivers Course

If you are following along with this free embedded Linux development course, you already have a Yocto build environment configured and a target machine selected. This lecture covers the part that actually matters most to a beginner: running the build, understanding what BitBake produces, and booting the result on QEMU so you can see your own embedded Linux system come alive. This is one of the most satisfying milestones in any free embedded systems course — the first time you watch your own custom-built kernel and root filesystem boot to a login prompt.

bitbake core-image-minimal runqemu deploy/images free linux device drivers course yocto build system

What You Will Learn

  • How BitBake resolves a target image into a full dependency graph and builds it
  • The difference between the common core-image targets and when to use each
  • How the build/tmp directory is organized, and where your final artifacts land
  • How to boot your freshly built image under QEMU, with and without graphics
  • Common build failures beginners hit, and how to read BitBake’s error output

Prerequisites

This lecture assumes you have already sourced your build environment setup script and have a working conf/local.MACHINE configuration pointing at a QEMU-emulated machine (qemuarm, qemuarm64, or qemux86-64). If you have not reached that stage yet in this free linux device drivers course, go back to the earlier lecture on configuring your build directory before continuing here.

Understanding BitBake Image Targets

BitBake does not build “a Linux system” in the abstract — it builds whatever image recipe you name on the command line, and that recipe pulls in every package it depends on, transitively, until BitBake has a complete dependency graph rooted at your target. When you type a build command, you are really telling BitBake: “resolve this recipe’s full dependency tree, and build every node in it, starting from whatever is missing.”

BitBake Dependency Resolution Flow
image recipe → toolchain (gcc, binutils) → C library → core packages → kernel recipe → root filesystem assembly → final images

A small set of image recipes cover almost every beginner use case. Here is how they compare on a modern Yocto release:

Image recipeContainsTypical use
core-image-minimalBusyBox-based console system, no GUIFastest build, sanity-testing a new BSP or a driver you are developing
core-image-minimal-initramfsSame content, packaged as a RAM-resident initramfsEarly boot debugging, recovery images, network-boot setups
core-image-satoFull graphical desktop with terminal, editor, file managerDemonstrating a complete graphical BSP, evaluation boards
core-image-full-cmdlineConsole system with a fuller userspace toolset than minimalDevelopment images where you want more debugging utilities available

For a first build, always reach for core-image-minimal. It exercises your entire toolchain and BSP layer without pulling in graphics stacks that can take hours on modest hardware.

Running the Build

With your environment sourced, kicking off a build is a single command:

$ bitbake core-image-minimal

The first invocation is the expensive one. BitBake has to fetch source for every recipe in the dependency graph, build a full cross-toolchain from scratch, then use that toolchain to cross-compile every package, and finally assemble a root filesystem image from the resulting packages. On a typical development laptop this can run well over an hour; on a CI build server with a shared sstate-cache it can drop to minutes. Do not interrupt a first build — let BitBake populate its shared-state cache, because every build after that reuses whatever hasn’t changed.

While the build runs, BitBake prints task-level progress such as do_fetch, do_compile, and do_package for each recipe. Watching this output teaches you the build pipeline faster than any diagram can — you can literally see the toolchain get built before the kernel, and the kernel get built before the root filesystem is assembled.

Where Your Build Artifacts Land

Once the build finishes, everything of interest lives under your build directory’s tmp subdirectory. Two subfolders matter most to a beginner:

Build Output Layout
tmp/work → per-recipe build and staging trees, including root filesystem assembly
tmp/deploy/images/<machine> → bootloader, kernel, and rootfs images ready for the target
tmp/deploy/rpm → every package produced by the build, in package form
tmp/deploy/licenses → extracted license text for every package pulled in

The deploy/images/<machine> folder is the one you actually care about day to day — it holds the flashable or bootable output of the entire build. The deploy/licenses folder matters more than most beginners realize: if you ever ship a product built on this image, that folder is your starting point for license compliance review.

Booting Your Image in QEMU

Yocto builds an internal copy of QEMU as part of the build, so you never need to install a distro QEMU package or worry about version mismatches between your host tools and your target emulation. The wrapper script that drives this internal QEMU is runqemu.

$ runqemu qemuarm

By default this opens a graphical framebuffer window and shows you the kernel boot log in real time, ending at a login prompt. Watching that boot log scroll by — udev starting, the root filesystem mounting, init entering its target runlevel — is genuinely useful debugging practice, because when something goes wrong in a real BSP bring-up later, this is exactly the log you will be reading.

Poky (reference distro) boot complete
target login: root
#

You can log in as root with no password on these development images — never do this on anything you actually ship. If you don’t have or don’t want a graphical window (headless servers, SSH sessions, CI runners), run headless instead:

$ runqemu qemuarm nographic

In this mode the boot log and console appear directly in your terminal, and you exit QEMU with Ctrl+A then X rather than closing a window. This is the mode you’ll use constantly once you move past the “does it boot” stage and into real driver bring-up, because it’s scriptable and works over SSH.

runqemu takes many more options than shown here — network configuration, serial-only consoles, custom kernel command-line arguments, and more:

$ runqemu help

A Worked Example: Confirming a Driver Change Took Effect

A common beginner workflow in this free linux kernel development course is: modify a kernel config or a device tree, rebuild, and confirm the change actually reached the running system. Here’s a minimal, original example that does exactly that using a trivial kernel command-line marker.

# Add a custom boot marker via the kernel command line
$ runqemu qemuarm bootparams="ep_marker=hello"

# Once booted, confirm it reached the running kernel
# target login: root
# cat /proc/cmdline
Expected output:
console=ttyAMA0 root=/dev/vda rw ep_marker=hello

Seeing ep_marker=hello echoed back in /proc/cmdline confirms the full pipeline worked end to end — your build, your bootloader, and your kernel all agree on what was passed. This exact pattern (inject something small and identifiable, then verify it downstream) is one you’ll reuse constantly once you start writing and testing your own device drivers.

Common Mistakes and Troubleshooting

Forgetting to re-source the build environment

Every new terminal session needs the environment setup script sourced again before BitBake or runqemu will work. A “command not found” error for bitbake almost always means this step was skipped.

Interrupting the first build

Killing a first-time build partway through can leave the shared-state cache in an inconsistent state for that recipe. If a rebuild behaves strangely afterward, clean just that recipe rather than assuming the whole build directory is corrupt.

Disk space exhaustion

A full Yocto build, including downloads and sstate-cache, can consume well over 50GB. Running out of disk space mid-build produces confusing, unrelated-looking failures — always check available space before a first build.

Best Practices

  • Keep a shared sstate-cache and downloads directory across build directories to avoid re-fetching and re-building unchanged components.
  • Build core-image-minimal first on any new machine or BSP before attempting heavier images — it isolates toolchain and BSP problems from application-level ones.
  • Read BitBake’s task log files directly (linked from the error output) rather than only the summary — the real error is almost always deeper in the log than the last printed line.

Performance Considerations

Build parallelism (BitBake’s own task parallelism and the underlying compiler’s job count) has the single biggest effect on build time on capable hardware. An under-provisioned tmpfs or a slow disk for the build directory is usually the next biggest bottleneck — the build directory performs an enormous amount of small file I/O.

Summary and Key Takeaways

  • BitBake builds whatever dependency graph your chosen image recipe defines, starting with the toolchain.
  • core-image-minimal is the right first target for any new machine or BSP.
  • Final artifacts live under tmp/deploy/images/<machine>.
  • runqemu boots your build with zero extra QEMU installation or version-matching work.

That completes the build-and-run milestone of this free embedded Linux course. From here, everything else — writing your own recipes, adding drivers, customizing the root filesystem — builds directly on the workflow you just practiced: change something, rebuild, boot it in QEMU, and verify.

Frequently Asked Questions

Why does the very first BitBake build take so long?

The first build has to fetch source for every recipe in the dependency graph and build a complete cross-toolchain from scratch before it can compile anything else. Subsequent builds reuse the shared-state cache and are dramatically faster.

Do I need to install QEMU separately for this free linux device drivers course?

No. Yocto builds its own internal QEMU as part of the build process, so there’s no separate package to install and no version-mismatch risk between host and target emulation.

What is the difference between core-image-minimal and core-image-sato?

core-image-minimal is a small console-only BusyBox-based system meant for fast sanity testing. core-image-sato is a full graphical desktop environment and takes considerably longer to build.

How do I exit QEMU when running in nographic mode?

Use the key sequence Ctrl+A followed by X. Closing the window works only in the graphical mode.

Where do the final kernel and root filesystem images end up?

Under tmp/deploy/images/<machine> inside your build directory, once the build completes successfully.

Can I log in as root with no password on these images?

Yes, but only on development/reference images like the ones built in this course. Never ship a production image with a passwordless root account.

What does the deploy/licenses folder contain?

Extracted license text for every package that went into the image — the starting point for any license compliance review before shipping a real product.

Continue This Free Embedded Linux Course

Next up: understanding Yocto layers and building your own custom layer from scratch.

Next Lecture Browse Full Course

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *