Create a Custom Layer
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-tegrawithout 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:
BBFILEStells BitBake where to find.bbrecipes and.bbappendextensions.BBFILE_COLLECTIONSregisters the layer's internal name.BBFILE_PATTERN_myproductassociates the collection with its directory.BBFILE_PRIORITY_myproductassigns priority6to the layer.LAYERDEPENDS_myproductdeclares that the layer requires thecoreandtegracollections.LAYERSERIES_COMPAT_myproductlimits 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.