Configuration
The Avocado config defines your project from board support to provisioning, and everything in between.
Precedence of supplied values
The Avocado CLI can be configured three ways, from highest to lowest precedence:
- CLI arguments
- Environment variables
- Configuration files
Configuration files
The Avocado CLI uses a single configuration file to define your project settings, build configurations, dependencies, and provisioning profiles.
By default, the CLI looks for a configuration file named avocado.yaml in the current working directory. This can be configured to use a different file name or location.
For detailed information about all available configuration options, see the config schema.
avocado init writes a starter avocado.yaml on distro.release: 2026 and distro.channel: edge. Its first line is a yaml-language-server comment that points editors at the JSON Schema, so they validate the file as you type. avocado config schema prints the schema the installed CLI uses.
Checking your config
The CLI warns about any key it ignores, whether it's misspelled, in the wrong place, or an old name that was renamed (extentions:). It suggests the closest valid key when one is near:
[WARNING] ./avocado.yaml: unknown key 'runtimes.dev.extentions' is ignored; did you mean 'extensions'?
These warnings go to stderr and never fail a command, so a file that built before still builds. avocado config show --output json includes them in a warnings array.
To check a config without building, run avocado config validate: YAML errors, schema violations and the CLI's own checks are errors (exit status 1), and ignored keys are warnings. It reads stdin with -c -, and --output json prints {ok, errors, warnings}.
The same schema powers editor support. Projects created with avocado init start with the yaml-language-server line, which gives VS Code (with the Red Hat YAML extension) and other YAML-language-server editors autocomplete, hover docs and validation. Add it to the top of an existing avocado.yaml to get the same:
# yaml-language-server: $schema=https://docs.peridio.com/schemas/avocado-config.json
Environment variables
Environment variables take precedence over configuration file values. When set, they override the corresponding config field.
| Environment variable | Config equivalent | Description |
|---|---|---|
AVOCADO_TARGET | default_target | Target architecture for builds and deployments. |
AVOCADO_TARGET_BOARD | default_target_board | Board variant within the target, feeding {{ avocado.target.board }} interpolation. |
AVOCADO_RUNTIME | default_runtime | Default runtime for commands that scope by runtime. Overrides default_runtime from config. |
AVOCADO_REPO_URL | distro.repo.url | Package repository URL. |
AVOCADO_RELEASEVER | distro.repo.releasever | DNF releasever override (e.g., 2024/edge). |
AVOCADO_DISTRO_RELEASE | distro.release | Distribution feed year (e.g., 2024). |
AVOCADO_DISTRO_CHANNEL | distro.channel | Distribution stability channel (e.g., edge, stable). |
AVOCADO_REPO_CA | distro.repo.ca | Path to a CA certificate bundle that the package feed's TLS certificate is checked against. |
AVOCADO_REPO_INSECURE | distro.repo.tls_verify | Set to 1, true or yes to skip TLS verification of the package feed. Testing only. |
AVOCADO_PARALLEL_TASKS | none | How many tasks run at once across the install DAG, the build DAG and the SDK phase. Defaults to min(cpus, 4), and to 1 under --runs-on. |
AVOCADO_NO_SESSION_CONTAINER | none | Set to 1 to run each install and build step in its own docker run instead of reusing one SDK container per command. For debugging a container an earlier step left in a bad state. |
Legacy environment variables
The following environment variables are deprecated but still supported as fallbacks:
| Legacy variable | Replacement |
|---|---|
AVOCADO_SDK_REPO_URL | AVOCADO_REPO_URL |
AVOCADO_SDK_REPO_RELEASE | AVOCADO_RELEASEVER |
These two are removed, not deprecated — they are no longer read at all:
| Removed variable | Replacement |
|---|---|
AVOCADO_FIT_KEY_DIR | runtimes.<name>.signing.fit_key |
AVOCADO_FIT_UNSIGNED | runtimes.<name>.signing.fit_unsigned |
Default runtime
Set default_runtime to avoid passing --runtime on every invocation. Resolution order from highest to lowest precedence:
- CLI
-r/--runtimeargument AVOCADO_RUNTIMEenvironment variabledefault_runtimeconfig value- Auto-resolved sole runtime (when exactly one runtime is defined)
default_runtime: dev
runtimes:
dev:
# ...
prod:
# ...
When default_runtime is set, the CLI validates at startup that it references a runtime defined in runtimes:.
Default target and board
Set default_target to avoid passing --target (-t) on every invocation. avocado init --target <target> writes it for you:
default_target: qemux86-64
Some targets are a System-on-Module (SoM) that ships on more than one carrier board. For those, the target alone is not enough — you must also set a board so the build knows which board's device tree and provisioning to use. The board feeds the {{ avocado.target.board }} interpolation and resolves from highest to lowest precedence:
--target-boardCLI flagAVOCADO_TARGET_BOARDenvironment variable- The resolved runtime's
target_board(when set on that runtime) - Top-level
default_target_board - Falls back to the resolved target when none of the above are set
The --target-board flag is available on build, install, provision, runtime build, runtime provision, sdk install, ext build, rootfs install and initramfs install.
Set both keys at the top of your avocado.yaml:
default_target: 'imx8mp-var-dart'
default_target_board: 'variscite-sonata'
Or scope the board to a single runtime, which wins over the top-level default when that runtime is selected:
default_target: 'imx8mp-var-dart'
runtimes:
dev:
target_board: 'variscite-sonata'
To override per command without editing config:
avocado build --target imx8mp-var-dart --target-board variscite-sonata
The per-target Get Started page and the target's hardware page call out when a board is required and which value to use.
Add-on feeds
distro.feeds lists the package feeds to enable, in dnf priority order (first wins). The distro feed comes first unless you list it. A name that repos: does not define names an add-on feed that the distro feed publishes for your target, such as ROS 2:
distro:
release: 2026
channel: edge
feeds:
- ros2-jazzy
An add-on needs no URL. It enables two repos off the distro feed's base URL, releasever and TLS settings: target/<target>-<name> for target installs and the SDK's target sysroot, and sdk/<target>-<name> for the SDK host. Before anything is written to avocado.lock, the CLI checks the name against the add-ons the channel publishes for the target (and, when the lock pins a feed snapshot, against that snapshot) and refuses one the target does not have, suggesting the nearest published name.
Connect configuration
The connect section stores defaults for Avocado Connect platform commands, so you don't need to pass --org and --project on every invocation. This section is typically populated by running avocado connect init.
connect:
org: my-org-id
project: my-project-id
server_key: <hex-encoded TUF public key>
| Field | Description |
|---|---|
org | Default organization ID for avocado connect commands |
project | Default project ID for avocado connect commands |
server_key | Connect server's TUF signing public key (hex). Per-runtime signing.server_key overrides this |
Runtime signing configuration
The per-runtime signing section names keys from the machine-local signing-key registry (avocado signing-keys). Only names appear in avocado.yaml; no key material is ever committed.
runtimes:
prod:
signing:
key: product-tuf # TUF metadata signing
fit_key: product-fit # boot FIT signing
fit_key_in_bootloader: true # default when fit_key is set
| Field | Default | Description |
|---|---|---|
key | Signing key for the runtime's TUF metadata. Required for Level 2 (user-controlled root) | |
content_key | key | Separate key for delegated-targets metadata only |
checksum_algorithm | sha256 | sha256 or blake3 |
server_key | Connect server's TUF signing public key (hex); overrides connect.server_key | |
fit_key | RSA PEM key that signs this runtime's boot FIT. A verity rootfs needs this or fit_unsigned, since the root hash rides in the FIT | |
fit_unsigned | false | Build the boot FIT unsigned. Mutually exclusive with fit_key |
fit_key_in_bootloader | true when fit_key is set | Also re-pack the feed's bootloader so U-Boot enforces fit_key. Set false to keep the distro bootloader |
fit_key replaces the AVOCADO_FIT_KEY_DIR environment variable and fit_unsigned replaces AVOCADO_FIT_UNSIGNED; neither variable is read any more. See Boot signing.
One entry or named entries
rootfs, initramfs, kernel and permissions each take either one configuration or a map of named ones. The CLI decides by the keys it finds:
- If any key is one of that section's fields (such as
packagesinrootfs, orusersinpermissions), the whole section is one configuration, nameddefault. Atarget-<name>:key next to the fields doesn't count as an entry name, so a per-target override block keeps the one-configuration form. - Otherwise every key is an entry name, and each entry is a configuration.
Mixing field keys with entry names is an error: permissions: is one block, because it sets a field, so 'prod' cannot be named entries. Use one form.
# One configuration: `users` is a permissions field
permissions:
users:
root:
password: ''
# Named entries: `dev` and `prod` are not permissions fields
permissions:
dev:
users:
root:
password: ''
prod:
users:
admin:
password: '$6$...'
Watch for misspellings here. permissions: { user: ... } has no permissions field, so it is read as an entry named user, not as users. The CLI warns about this: 'permissions.user' sets no permissions fields, so it is read as a named permissions entry; did you mean the field 'users'?
Named entries are referenced by name, for example permissions: prod on a rootfs, or kernel: yocto-6-6 on a runtime. For rootfs and initramfs, only the entry named default (or the only entry) is built, and overlay, post_install, image and source are read only in the one-configuration form, so use that form for images.
Rootfs configuration
The rootfs section configures the shared rootfs sysroot used by runtime builds.
rootfs:
filesystem: erofs-lz4 # default
overlay: overlays/rootfs # optional: copied into the sysroot after package install
permissions: dev # a profile from the top-level permissions section
packages:
avocado-pkg-rootfs: '*'
| Field | Default | Description |
|---|---|---|
packages | avocado-pkg-rootfs | Map of package names to version constraints. When no packages are set, the rootfs installs avocado-pkg-rootfs |
filesystem | erofs-lz4 | Image format: erofs-lz4 or erofs-zst |
overlay | Path or object. See Overlay below | |
permissions | Users and groups for the image: the name of a permissions profile, or an inline profile | |
post_install | Script, relative to src_dir, run on the image work directory just before the filesystem is created. It replaces the built-in steps (usrmerge symlinks, removing /media, /mnt, /srv and the contents of /boot, an empty /etc/machine-id, systemd presets, ldconfig) | |
image | Image wrapper options. See Image options below | |
source | { type: path, path: <dir> } reads the whole section from <dir>/avocado.yaml (or avocado.yml) instead, with <dir> relative to src_dir |
A target-<name>: block inside rootfs overrides settings for that target, but only post_install and image are taken from it.
Initramfs configuration
The initramfs section configures the initramfs sysroot used for the early boot environment. It takes the same fields as rootfs: overlay, permissions, post_install, source and target-<name>: blocks work the same way. These differ:
initramfs:
filesystem: cpio.zst # default
overlay: overlays/initramfs # optional: copied into the sysroot after package install
permissions: dev
packages:
avocado-pkg-initramfs: '*'
| Field | Default | Description |
|---|---|---|
packages | avocado-pkg-initramfs | Map of package names to version constraints. When no packages are set, the initramfs installs avocado-pkg-initramfs |
filesystem | cpio.zst | Image format: cpio, cpio.zst, cpio.lz4, or cpio.gz |
image | Wrapper options (type, args only). See Image options below |
Image options
The image block is accepted on rootfs, on initramfs, and on each extension. It configures how the produced filesystem image is wrapped and protected.
Not every field applies everywhere. type and args are read for all three. verity is read for rootfs and for extensions only; the initramfs build does not consult it.
rootfs:
image:
verity: true
extensions:
my-ext:
image:
type: kab # "kab" wraps the image with kabtool; absent or "raw" keeps the raw artifact
args: '-b -t kos.layer.basefs'
verity: true
| Field | Default | Description |
|---|---|---|
type | raw | kab wraps the produced image with kabtool; raw (or absent) keeps the raw artifact |
args | Extra arguments passed to kabtool when type: kab | |
verity | false | Produce a dm-verity hash tree and root hash for this image. Read for rootfs and extensions only; the initramfs build does not consult it — see Filesystem verity |
verity is a strict boolean: "true", 1 and yes are rejected rather than coerced, because a security opt-in should not be decided by a value that only looks true.
Overlay
The overlay field on rootfs and initramfs merges a source directory from your project into the sysroot after package installation. Two forms are supported:
# Short form — merge mode (keeps the overlay files' modes)
rootfs:
overlay: overlays/rootfs
# Long form — explicit mode
initramfs:
overlay:
dir: overlays/initramfs
mode: opaque # new files get default modes and fresh timestamps
| Field | Default | Description |
|---|---|---|
dir | overlay | Path relative to project root (src_dir) |
mode | merge | merge keeps each overlay file's mode; opaque applies the default (umask-filtered) mode and a fresh timestamp |
preprocess | false | Run {{ }} interpolation over overlay files: true for all UTF-8 files, or a list of globs |
The string short form is equivalent to { dir: "...", mode: "merge" }. The overlay path is resolved as /opt/src/<path> inside the SDK container. Overlay config participates in the sysroot stamp, so changes trigger a reinstall.
In both modes the overlay is copied over the sysroot. Paths the overlay creates are owned by root; directories that already exist keep their mode and owner, and nothing else in the sysroot is touched. Each install records the files the overlay placed and, on the next install, removes the ones the overlay no longer has (printing Removed /<path>); a path a package owns is kept, with a notice. An overlay directory that would replace a symlink or file in the sysroot fails the install.
Permissions
The permissions section defines the users and groups baked into an image's /etc/passwd, /etc/shadow and /etc/group at build time. A rootfs or initramfs picks a profile through its permissions field, by name or inline. Projects created with avocado init start with a dev profile:
rootfs:
permissions: dev
initramfs:
permissions: dev
permissions:
dev: # NOT FOR PRODUCTION: empty root password
users:
root:
password: ''
prod:
users:
admin:
password: '$6$...' # a crypt hash, as written to /etc/shadow
shell: /bin/sh
groups: [admin, wheel]
groups:
admin:
gid: 1000
Groups are created before users, so a user can name a group defined in the same profile.
User fields
| Field | Description |
|---|---|
password | Password hash written to /etc/shadow. An empty string means no password, and the build log warns about it. Defaults to *, which disables password login |
uid | User ID. When omitted, assigned from a counter starting at 1000 that doesn't skip IDs already in the base image |
gid | Primary group ID written to /etc/passwd. When omitted, the user takes the GID of the first entry in groups, which must exist by then or the build fails. With no groups either, the user gets a group of its own name, created with the UID as its GID if it doesn't exist |
groups | The first entry is the primary group (see gid); the user is not added to it as a member. The user joins each remaining group. A group that doesn't exist is skipped with a warning |
home, shell, gecos | Home directory (default /home/<name>), login shell (default /bin/sh) and full-name field (default the user name) |
last_change, min_days, max_days, warn_days, inactive_days, expire_date | Password aging fields for /etc/shadow, as integers |
A user that already exists in the base image keeps its /etc/passwd entry; only its /etc/shadow entry is rewritten.
Group fields
A group's value can be an object with these fields, or empty for a group with an automatically assigned GID.
| Field | Description |
|---|---|
gid | Group ID. When omitted, assigned from a counter starting at 1000. For a group already in the base image, a gid that differs from the existing one fails the build |
members | Users in the group. For a group already in the base image, this replaces its member list |
A user's disabled and a group's password have no effect, and the CLI warns if you set them. system on a user or group only labels the build log; it doesn't change ID allocation.
Kernel configuration
The top-level kernel: block defines one or more kernel configurations that runtimes can reference. Two forms are accepted:
# Singleton form — synthesized as the implicit "default" kernel
kernel:
package: kernel-image
version: '*'
# Named map form — multiple kernels for different runtimes
kernel:
yocto-6-6:
package: kernel-image-6.6
version: '*'
yocto-5-15:
package: kernel-image-5.15
version: '*'
Kernel config fields
Two mutually exclusive modes:
Package mode — kernel installed from an RPM during runtime install:
| Field | Description |
|---|---|
package | Package name (e.g. kernel-image). Mutually exclusive with compile |
version | Version constraint (e.g. *, 6.6.*). Used with package |
Compile mode — kernel cross-compiled via sdk.compile.<section> during runtime build:
| Field | Description |
|---|---|
compile | SDK compile section name (references sdk.compile.<section>). Mutually exclusive with package |
install | Install script path — copies kernel artifacts to runtime build dir. Required when compile is set |
Command line (either mode, or on its own):
| Field | Description |
|---|---|
cmdline | Complete kernel command line, replacing the platform's own. Mutually exclusive with cmdline_extra |
cmdline_extra | Arguments appended to the platform's command line (e.g. isolcpus=4-6, earlycon). Mutually exclusive with cmdline |
A kernel: block that sets only cmdline or cmdline_extra is valid, and it does not change where the kernel comes from. The kernel is still selected by the runtime's named reference, the top-level entry, or the platform, exactly as if the block were absent; the project is only changing how that kernel boots. Precedence is per concern: a runtime's cmdline / cmdline_extra wins over the top-level one, while the kernel itself keeps resolving by its own rules. Both resolve per target, so a target-<name>: override at either level is honored. The command line reaches the build and provision hooks as AVOCADO_KERNEL_CMDLINE / AVOCADO_KERNEL_CMDLINE_EXTRA, and editing it alone invalidates the runtime build.
On machines that boot a FIT, the runtime build writes cmdline_extra into every FIT configuration node as avocado,cmdline-extra, covered by the FIT signature. The build fails when cmdline_extra is set without signing.fit_key or signing.fit_unsigned, when the runtime builds no initramfs, or when the value holds a ", ', \, #, & or newline.
Runtime kernel references
A runtime can reference a named top-level kernel entry or provide an inline override:
runtimes:
dev:
kernel: yocto-6-6 # named reference (string)
prod:
kernel: # inline override (object)
package: kernel-image
version: '6.6.*'
rt:
kernel: # command line only; the kernel itself resolves as if this block were absent
cmdline_extra: 'isolcpus=4-6 nohz_full=4-6'
When kernel: is omitted on a runtime, the CLI falls back to the top-level default kernel entry, or — when no top-level kernel is configured — to the avocado-runtime meta-package's legacy bootfiles behavior.
Var partition configuration
The runtime var section configures the btrfs-formatted /var partition: a default compression algorithm and a map of subvolumes keyed by path (relative to the var root). Subvolumes can also be declared at the extension level, in which case they travel with the extension; runtime-level entries deep-merge on top and win on conflict.
extensions:
my-ext:
subvolumes:
lib/docker:
nodatacow: true
quota: '10G'
lib/myapp/cache: true # shorthand: writable with defaults
runtimes:
dev:
extensions: [my-ext]
var:
compression: 'zstd:3'
subvolumes:
lib/logs:
writable: true
compression: 'zstd:9' # per-subvolume override
lib/docker:
writable: false # override extension: make read-only
A built-in lib/avocado subvolume is always present (writable). Declaring it explicitly in runtime var.subvolumes suppresses the nested-path warning for extensions that live under it.
var fields
| Field | Default | Description |
|---|---|---|
compression | none | Partition-wide default compression (e.g. zstd, zstd:3, lzo, zlib:6, no). Overridden by per-subvolume set |
subvolumes | Map of path → subvolume entry. Paths are relative to the var root | |
encrypt | false | Encrypt /var as a LUKS2 container keyed to the device's hardware key store |
hardware | auto | Which key engine binds it: auto, caam, tpm2, or none. caam/tpm2 fail the boot closed if that engine cannot hold a keyslot; none requires recovery |
recovery | Name of a registry secret (signing-keys create <name> --algorithm hmac-sha256) that is the master for an operator-held recovery keyslot |
encrypt, hardware and recovery are validated at config load — recovery without encrypt, an unknown hardware value, or hardware: none without recovery are all errors. See Encrypted /var for the full workflow.
Subvolume entry forms
Each map value can be a shorthand or a full object:
| Form | Meaning |
|---|---|
true | Enabled, writable, inherits partition defaults |
false | Disabled — suppresses an extension-declared subvolume from a runtime |
"ro" | Enabled, read-only, inherits partition defaults |
| object (see below) | Full configuration |
Subvolume object fields
| Field | Default | Description |
|---|---|---|
writable | true | false makes the subvolume read-only (applied post-mkfs via btrfs property set ro true) |
compression | inherited | Per-subvolume compression. Ignored (and skipped) when nodatacow: true — NOCOW disables COW |
nodatacow | false | Set the NOCOW inode flag via chattr +C. Useful for databases, VM images, and other random-write workloads |
quota | none | Qgroup size limit (e.g. 500M, 5G, none). Enables btrfs quotas on the partition when any subvolume sets one |
enabled | true | Set to false to suppress an extension-declared subvolume from a runtime |
Resolution order
- Built-in
lib/avocado(writable). - Extensions from the runtime's extension list, in order — first-listed wins on path conflicts (a warning is emitted for collisions).
- Runtime
var.subvolumesdeep-merges on top, always winning. - Partition-level
var.compressionapplied as the default for subvolumes that don't set their own. - Entries with
enabled: falseare filtered out.
Subvolume config is included in the runtime input hash, so changes trigger a proper var-image rebuild.
Examples
Example configuration files are available in the Avocado OS repository. These examples demonstrate common configuration patterns for different use cases and target platforms.