Images and Packages

How Yocto images are built from packages and how to control what goes into them

One of the most important things to understand in the Yocto Project is the relationship between images and packages.

People often start by thinking of an image as a single build target, but in Yocto an image is really the result of selecting:

  • a set of packages
  • a package manager or package format
  • image features
  • output formats such as wic, ext4, or tar.gz

Once you understand that relationship, it becomes much easier to build clean, maintainable system images.

The Basic Idea

At a high level:

That means your image usually should not contain lots of custom logic itself. Instead, the image should select the packages that make up the system.

Recipes, Packages, and Images

It helps to separate these terms clearly.

Recipe

Metadata that tells BitBake how to fetch, configure, compile, install, and package some software.

Package

An installable output produced from a recipe.

Image

A root filesystem and related output files built by installing selected packages into a final system image.

For example, a recipe called myapp.bb might produce a debug build and documentation:

  • myapp
  • myapp-dbg
  • myapp-doc

An image installs the package myapp or myapp-dbg, not the recipe name as an abstract concept. Normally, the binary package will be the same as the recipe name.

What an Image Recipe Looks Like

A simple image recipe might look like this:

SUMMARY = "My example image"
LICENSE = "MIT"

inherit core-image

IMAGE_INSTALL:append = " myapp i2c-tools"

This inherits the standard core-image behaviour, which defines it as an image recipe and then adds packages to the image.

IMAGE_INSTALL

The most common way to define the image contents is with IMAGE_INSTALL in an image recipe. This is a list (space separated) of packages that will be installed in the final image.

IMAGE_INSTALL:append = " htop strace"

This adds htop and strace packages to the image.

You can also use EXTRA_IMAGE_INSTALL outside an image recipe, for example to quickly test add a package in your local.conf.

Finding packages to install

There are many, many packages that are provided by the community. Before writing your own recipe, it is sensible to check if one already exists.

  1. Go to https://layers.openembedded.org/layerindex/branch/master/recipes/
  2. Select your Yocto Project release branch
  3. Search for your your package

You will get a list of matching recipes and the layer that defines them - you will need to add that layer to your project if you have not already done so.

Find the Layer

Click on the link of the layer name

This takes you to a page about the layer and most importantly, the location of the repository so that you can clone it.

Clone the layer.

git clone -b <branch> <repo> layers/third-party/<layer name>

Add the layer to your project

bitbake-layers add-layer layers/third-party/<layer name>

Add the package to your image

Now you can add the package to your IMAGE_INSTALL variable.

Core Image Features

You can also influence image contents through IMAGE_FEATURES (if set in an image recipe) or EXTRA_IMAGE_FEATURES (if set outside an image recipe, such as in local.conf).

For example:

IMAGE_FEATURES += " ssh-server-openssh"
EXTRA_IMAGE_FEATURES += " debug-tweaks"

These are not arbitrary package names. They are feature flags that expand to specific behaviour or package selections. They may pull in specific packages but also can trigger flags within some recipes.

Some common examples are:

  • debug-tweaks
  • package-management
  • read-only-rootfs
  • ssh-server-dropbear
  • ssh-server-openssh

See https://docs.yoctoproject.org/current/ref-manual/features.html#image-features for more details.

The Difference Between IMAGE_FEATURES and IMAGE_INSTALL

Use IMAGE_FEATURES when you want to enable a supported image-level behaviour.

Use IMAGE_INSTALL when you want to install specific packages.

For example:

  • use IMAGE_FEATURES += "ssh-server-openssh" to request SSH support as an image feature
  • use IMAGE_INSTALL:append = " openssh-sftp-server" if you specifically need one package

Base Images

You do not have to start every image from scratch.

Yocto already provides some base images, such as:

  • core-image-base
  • core-image-minimal

You can either build those directly or create your own image recipe that inherits from core-image.

Example: A Development Image and a Production Image

It is common to maintain more than one image for a product.

For example:

Production image

SUMMARY = "Production image"
LICENSE = "MIT"

inherit core-image

IMAGE_INSTALL:append = " myapp"
IMAGE_FEATURES += " read-only-rootfs"

Development image

SUMMARY = "Development image"
LICENSE = "MIT"

inherit core-image

IMAGE_INSTALL:append = " myapp gdbserver strace"
IMAGE_FEATURES += " ssh-server-openssh"
EXTRA_IMAGE_FEATURES += " debug-tweaks"

This is a good pattern because:

  • the production image stays small and focused
  • the development image carries the debugging tools
  • each image clearly expresses its purpose

Package Formats

Images can also be tied to a package format depending on how your system is meant to be updated or managed.

Common formats include:

package_rpm

RedHat Packages - defined by RedHat, Fedora, CentOS, etc

package_deb

Debian Packages - defined by Debian, Ubuntu, etc

package_ipk

Opkg Packages - A light-weight package manager, optimised for embedded systems

These are selected through:

PACKAGE_CLASSES ?= "package_ipk"

This affects how packages are emitted during the build and whether package management can be used on the target.

Enable Package Management on the Target

If you want to install or update packages on the running target, you will need to enable package management support in the image.

IMAGE_FEATURES += " package-management"

Without that, packages may still be built during the Yocto build, but the target image may not include the tooling or metadata needed to manage them at runtime.

Image Output Formats

The image is not just a root filesystem directory. Yocto can create several final artifacts depending on what you need.

For example:

IMAGE_FSTYPES += " wic.gz ext4"

would produce a full disk image (the ‘wic’ image), which contains the partitions, bootloader, etc and would also create an ext4 formatted image of the root filesystem.

Common output formats include:

  • tar.gz
  • ext4
  • wic
  • cpio.gz

The right choice depends on how the image will be deployed:

  • SD card image
  • eMMC image
  • initramfs
  • update bundle

Where the Files Go

After the build, the image artifacts are typically written under:

tmp/deploy/images/<machine>/

You will usually find:

  • kernel images
  • device trees
  • bootloader artifacts
  • root filesystem images
  • package feed artifacts

Common Mistakes

Installing Too Much in the Image Recipe

If your image recipe contains a huge list of packages, it becomes hard to maintain.

That is often a sign that you should:

  • split development and production images
  • move reusable policy into the distro layer

We will look at creating package groups later.

Confusing Recipes with Packages

Do not forget that recipes may generate several packages.

If something does not appear in the image as expected, check what packages the recipe actually produced.

Using local.conf for Long-Term Image Design

It is fine to experiment in local.conf, for example:

IMAGE_INSTALL:append = " vim"

But long-term project image definitions should live in your own image recipes or project metadata, not only in local developer configuration.

Useful Debugging Commands

To list the packages built by a recipe:

bitbake -e myapp | less

bitbake -e shows all of the variables used by a build.

Look for variables such as:

  • PACKAGES
  • FILES:${PN}
  • RDEPENDS:${PN}

To inspect the final image contents after a build, you can also look under the root filesystem area in tmp/work/... or inspect the built image directly.

A Maintainable Pattern

A good general structure is:

  • recipes describe how to build software
  • package groups define reusable software sets
  • distro configuration defines platform-wide policy
  • image recipes describe the final system role

That separation makes it easier to understand why a package is present and where a change should really be made.

Summary

Images are built by installing packages into a final root filesystem.

The main ideas from this lesson are:

  • recipes produce packages, and images install packages
  • use IMAGE_INSTALL to add specific packages
  • use IMAGE_FEATURES for supported image-level behaviour
  • use package groups to keep image recipes manageable
  • keep development and production image roles separate where possible
  • use PACKAGE_CLASSES and IMAGE_FSTYPES to control output behaviour

Quick quiz: images and packages

A quick review of how image composition works in Yocto.

Question 1 A team keeps putting build logic directly into image recipes for every application they ship. What is the cleaner mental model from this lesson?
Question 2 When would `IMAGE_FEATURES` be a better fit than adding a specific package to `IMAGE_INSTALL`?
Question 3 Your image recipe is becoming an unreadable package list shared across several products. What is the most maintainable next step?