Mental model
Most Avocado gotchas make sense once you know which of three places a piece of work happens in. Each one sees different files and different environment variables, and keeps different state.
Three places code runs
Section titled “Three places code runs”| Where | What runs there | What it sees |
|---|---|---|
| Your host | The avocado CLI: parses avocado.yaml, runs templating, resolves the kernel version, writes avocado.lock, decides what’s up to date |
Your project directory and your shell’s environment |
The SDK container (docker.io/avocadolinux/sdk:<tag>) |
dnf installs, your compile/install/post_build scripts, overlay copies, mkfs.erofs, the Tegra flash tooling |
Your project mounted at /opt/src, and the build volume mounted at /opt/_avocado |
| The device | avocadoctl merging extensions, on_merge commands, systemd, your services |
Only what’s in the images you shipped |
Some consequences:
{{ env.X }}inavocado.yamlreads your host’s environment when the CLI loads the config. Your scripts, which run in the container, see a different set of variables. See Templating and Environment variables.- Overlay files are copied inside the container. Only overlays that opt in with
preprocess:get template substitution, and that happens on the host first. See Templating. - On the device, nothing from
avocado.yamlexists any more.on_merge,enable_servicesandmodprobesurvive only as lines in each extension’s release file. See on_merge and the release file.
Where state lives
Section titled “Where state lives”| State | Location | Survives avocado clean? |
|---|---|---|
avocado.yaml, overlays, scripts |
Your project directory | Yes |
avocado.lock (resolved package versions, feed snapshot, kernel pin) |
Your project directory. The official docs still say .avocado/lock.json, which is out of date. |
Yes. Clear it with avocado unlock or avocado clean --unlock. |
.avocado/ (overlay staging, provision state) |
Your project directory | Yes. avocado clean only removes the volume and .avocado-state. Delete it by hand. |
| Sysroots, stamps, built images | The Docker volume avo-<uuid>, mounted at /opt/_avocado in the SDK container |
Removed |
.avocado-state |
Your project directory. It records which volume belongs to this project. | Removed; the next command creates a new volume and a new file |
The build volume is laid out per target:
/opt/_avocado/<target>/├── .stamps/ # "is this step up to date" records├── sdk/<host-arch>/ # SDK toolchain and host tools├── sdk/target-sysroot/ # headers/libs for cross-compiling├── rootfs/ # rootfs sysroot (packages + overlay)├── initramfs/ # initramfs sysroot├── kernel/ # the pinned kernel's files├── includes/<ext>/ # avocado.yaml + stone files of fetched extensions├── extensions -> runtimes/<r>/extensions # legacy compatibility symlink├── runtimes/<runtime>/│ ├── extensions/<ext>/ # one sysroot per extension, per runtime│ ├── extensions/<ext>-<ver>.raw│ └── ...kernel, initramfs, rootfs image, flash inputs└── output/ ├── extensions/<ext>-<ver>.raw # old versions are never removed └── runtimes/<runtime>/{os-bundle.aos, stone/}On macOS and Windows, Docker runs inside the avocado-vm helper VM, not Docker Desktop. See Inspecting the build volume for how to look inside it.
What ends up on the device
Section titled “What ends up on the device”A runtime is what you deploy. It’s made of:
- Kernel plus initramfs (a cpio archive).
- Rootfs: a read-only EROFS image. Its
/etcis read-only too, which matters formachine-id. See Hostname patterns and machine-id. - Extensions: EROFS images merged over the rootfs at boot. A
sysextoverlays/usrand/opt. Aconfextoverlays/etc. An extension can be both, and the CLI builds both by default. - The var partition: btrfs and writable. It persists across updates, and it’s where extension images and runtime manifests live (
/var/lib/avocado).
Extensions are merged by avocadoctl, not by stock systemd-sysext, which Avocado disables. Early boot services like systemd-modules-load and systemd-sysctl have no ordering against that merge, and in practice they run before it. See Boot and extension merge.
Three kinds of “up to date”
Section titled “Three kinds of “up to date””The CLI keeps three separate kinds of state. None of them checks the others:
- The lock pins which package versions to install.
- Stamps record which inputs each build step last succeeded with.
- Sysroots hold the actual files. Updates are applied on top of what’s already there.
A step is skipped when its inputs hash the same as last time. Nothing verifies that a sysroot or image actually contains what those inputs describe. That’s the root of most “my change didn’t take effect” problems; see Build state and stale sysroots.