Jump to content

Create a Custom Layer

From RidgeRun Developer Wiki
Yocto Documentation
Release 2026


Follow us on: YouTube Twitter LinkedIn Email Share this page

Share This Page

Preferred Partner Logo 3 Partner Program Banner



One of the most common questions people ask when they start working with Yocto is: Why should I create my own layer?

The main reason is to keep a clear separation between our product code and the code we receive from other projects layers (e.g meta-tegra).

We should not add product customizations directly to meta-tegra, OpenEmbedded Core, or any other external layer. If we modify those layers, our changes become mixed with upstream changes. This makes updates harder and can cause conflicts when we integrate new versions published by the maintainers.

A custom layer allows us to:

  • keep our recipes and configuration separate from upstream;
  • update meta-tegra without mixing it with internal changes;
  • clearly identify which metadata belongs to our product;
  • version our customizations independently;
  • align the layer history with the product release cycle;
  • reuse the same configuration in different build environments.

For these reasons, the first step in this guide is to create our own layer. We will then integrate it with meta-tegra, confirm that BitBake detects both layers, and build an image for an NVIDIA Jetson device.

What you will have by the end of this Wiki

This guide creates a Wrynose build environment. When we finish, our workspace will have the following structure:

yocto-tegra/
├── .venv/
├── meta-tegra-wrynose.conf.json
├── meta-myproduct/
└── bitbake-builds/
    └── meta-tegra-wrynose/
        ├── build/
        ├── config/
        └── layers/
            ├── bitbake/
            ├── openembedded-core/
            └── meta-tegra/

The myproduct names are examples. Each team should replace them with the name of its company, platform, or product.

Important: all repositories in a build must use compatible branches. This guide intentionally selects Wrynose for OpenEmbedded Core and meta-tegra, and BitBake 2.18. For other combinations, read Which branch should I use?.

1. Install bitbake-setup

Create the workspace and install bitbake-setup in a Python virtual environment:

export YOCTO_WORKSPACE="$HOME/yocto-tegra"

mkdir -p "$YOCTO_WORKSPACE"
python3 -m venv "$YOCTO_WORKSPACE/.venv"
source "$YOCTO_WORKSPACE/.venv/bin/activate"
python -m pip install --upgrade pip bitbake-setup

Activate this virtual environment again whenever you open a new terminal and need to run bitbake-setup:

export YOCTO_WORKSPACE="$HOME/yocto-tegra"
source "$YOCTO_WORKSPACE/.venv/bin/activate"

Use the same workspace path chosen when creating the environment.

2. Define the Wrynose build configuration

Create $YOCTO_WORKSPACE/meta-tegra-wrynose.conf.json with the following content:

{
  "version": "1.0",
  "description": "OpenEmbedded build with meta-tegra for Yocto Wrynose",
  "sources": {
    "bitbake": {
      "git-remote": {
        "uri": "https://git.openembedded.org/bitbake",
        "branch": "2.18",
        "rev": "2.18"
      },
      "path": "bitbake"
    },
    "openembedded-core": {
      "git-remote": {
        "uri": "https://git.openembedded.org/openembedded-core",
        "branch": "wrynose",
        "rev": "wrynose"
      },
      "path": "openembedded-core"
    },
    "meta-tegra": {
      "git-remote": {
        "uri": "https://github.com/OE4T/meta-tegra.git",
        "branch": "wrynose",
        "rev": "wrynose"
      },
      "path": "meta-tegra"
    }
  },
  "bitbake-setup": {
    "configurations": [
      {
        "name": "meta-tegra-wrynose",
        "description": "Yocto Wrynose build for NVIDIA Jetson platforms",
        "setup-dir-name": "meta-tegra-wrynose",
        "bb-layers": [
          "openembedded-core/meta",
          "meta-tegra"
        ]
      }
    ]
  }
}

This configuration tells bitbake-setup to obtain BitBake, OpenEmbedded Core, and meta-tegra from separate repositories. It also adds the OpenEmbedded Core and meta-tegra layers to the generated bblayers.conf.

3. Initialize the build setup

Run bitbake-setup from the workspace:

cd "$YOCTO_WORKSPACE"

bitbake-setup \
    --setting default top-dir-prefix "$YOCTO_WORKSPACE" \
    --setting default top-dir-name bitbake-builds \
    init \
    --setup-dir-name meta-tegra-wrynose \
    "$YOCTO_WORKSPACE/meta-tegra-wrynose.conf.json"

If prompted to select a configuration, choose meta-tegra-wrynose.

The command creates the setup under:

$YOCTO_WORKSPACE/bitbake-builds/meta-tegra-wrynose/

Load its BitBake environment:

source \
    "$YOCTO_WORKSPACE/bitbake-builds/meta-tegra-wrynose/build/init-build-env"

This command changes the current directory to the generated build directory. In a new terminal, restore the workspace variable and load both environments before using bitbake or bitbake-layers:

export YOCTO_WORKSPACE="$HOME/yocto-tegra"
source "$YOCTO_WORKSPACE/.venv/bin/activate"
source "$YOCTO_WORKSPACE/bitbake-builds/meta-tegra-wrynose/build/init-build-env"

Use your actual workspace path if it differs from this example.

4. Create the custom layer

With the BitBake environment loaded, create meta-myproduct outside the repositories managed by bitbake-setup:

bitbake-layers create-layer \
    --layerid myproduct \
    --priority 6 \
    "$YOCTO_WORKSPACE/meta-myproduct"

The --layerid myproduct and --priority 6 options make the internal collection name and priority explicit. The generated structure includes:

meta-myproduct/
├── conf/
│   └── layer.conf
├── COPYING.MIT
├── README
└── recipes-example/
    └── example/
        └── example_0.1.bb

Configure layer.conf

Review meta-myproduct/conf/layer.conf and make sure it contains an equivalent configuration:

BBPATH .= ":${LAYERDIR}"

BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
            ${LAYERDIR}/recipes-*/*/*.bbappend"

BBFILE_COLLECTIONS += "myproduct"
BBFILE_PATTERN_myproduct = "^${LAYERDIR}/"
BBFILE_PRIORITY_myproduct = "6"

LAYERDEPENDS_myproduct = "core tegra"
LAYERSERIES_COMPAT_myproduct = "wrynose"

Each entry has a specific purpose:

  • BBFILES tells BitBake where to find .bb recipes and .bbappend extensions.
  • BBFILE_COLLECTIONS registers the layer's internal name.
  • BBFILE_PATTERN_myproduct associates the collection with its directory.
  • BBFILE_PRIORITY_myproduct assigns priority 6 to the layer.
  • LAYERDEPENDS_myproduct declares that the layer requires the core and tegra collections.
  • LAYERSERIES_COMPAT_myproduct limits the layer to Wrynose.

The Wrynose branch of meta-tegra uses the tegra collection with priority 5. A priority of 6 gives an equivalent recipe in meta-myproduct higher layer priority.

5. Add the custom layer

bitbake-setup already added meta-tegra from the JSON configuration. Add the custom layer to the generated build configuration:

bitbake-layers add-layer "$YOCTO_WORKSPACE/meta-myproduct"

The relevant entries in build/conf/bblayers.conf will be similar to:

BBLAYERS ?= " \
  /home/user/yocto-tegra/bitbake-builds/meta-tegra-wrynose/layers/openembedded-core/meta \
  /home/user/yocto-tegra/bitbake-builds/meta-tegra-wrynose/layers/meta-tegra \
  /home/user/yocto-tegra/meta-myproduct \
  "

Do not copy /home/user literally. The generated file uses paths from your workspace.

For a training exercise, adding the custom layer with bitbake-layers keeps the steps visible. For a product configuration, also declare the custom layer in a project-owned bitbake-setup configuration. Otherwise, bitbake-setup update --update-bb-conf yes can regenerate bblayers.conf without the manually added entry.

6. Configure the Jetson machine

Edit the generated build/conf/local.conf and set the machine that matches the target hardware:

MACHINE = "jetson-agx-orin-devkit"

List the available machines with:

find \
    "$YOCTO_WORKSPACE/bitbake-builds/meta-tegra-wrynose/layers/meta-tegra/conf/machine" \
    -maxdepth 1 -type f -name '*.conf' -printf '%f\n' | sort

The value assigned to MACHINE is the machine file name without the .conf extension.

7. Create an image in the custom layer

Create the image recipe directory:

mkdir -p "$YOCTO_WORKSPACE/meta-myproduct/recipes-core/images"

Then create meta-myproduct/recipes-core/images/myproduct-image.bb:

require recipes-core/images/core-image-minimal.bb

SUMMARY = "Minimal image for the MyProduct Jetson platform"

This image reuses core-image-minimal. Later, add product packages without modifying OpenEmbedded Core or meta-tegra:

IMAGE_INSTALL:append = " myproduct-application"

The myproduct-application package must have its own recipe before you add it to the image.

8. Run basic checks

Confirm that BitBake detects both layers

bitbake-layers show-layers

The output must contain rows equivalent to:

layer                 path                                      priority
========================================================================
tegra                 .../layers/meta-tegra                           5
myproduct             .../meta-myproduct                              6

Confirm that BitBake finds the custom image

bitbake-layers show-recipes myproduct-image

The output should identify meta-myproduct as the layer providing the image:

myproduct-image:
  meta-myproduct       1.0

This command shows the layer and recipe version, not the full recipe path.

Confirm the selected machine

bitbake-getvar MACHINE

For this example, the effective value must be:

MACHINE="jetson-agx-orin-devkit"

9. Build the image

After the checks pass, build the image:

bitbake myproduct-image

The artifacts will be available under:

$YOCTO_WORKSPACE/bitbake-builds/meta-tegra-wrynose/build/tmp/deploy/images/jetson-agx-orin-devkit/

In addition to the root filesystem, meta-tegra adds the image types required for Jetson. The output should include a flashing package with a name similar to:

myproduct-image-jetson-agx-orin-devkit.rootfs.tegraflash-tar.zst

See Flashing Basics for instructions on using this file.

Cookies help us deliver our services. By using our services, you agree to our use of cookies.