Build images¶
OVOS images are built with Docker Buildx Bake. The wrapper script
scripts/bake.sh sets sane defaults, handles multi-arch builds, and wires the
base image dependencies defined in docker-bake.hcl.
Run from the repo root
scripts/bake.sh expects to be run from the repository root where
docker-bake.hcl lives.
Docker Buildx required
Builds require Docker with Buildx (BuildKit). Podman works for running images, but builds use Docker Buildx Bake.
Registry defaults
Buildx Bake defaults to docker.io/smartgic, matching the compose bundles.
If you publish to a different registry, make sure your compose files point
to it.
Quick examples¶
./scripts/bake.sh --load --no-push
TAG=alpha CHANNEL=alpha VERSION=alpha PLATFORMS=linux/amd64,linux/arm64 ./scripts/bake.sh
TAG=stable CHANNEL=stable ./scripts/bake.sh
./scripts/bake.sh -T services
REGISTRY=docker.io/smartgic TAG=alpha CHANNEL=alpha ./scripts/bake.sh
Targets and groups¶
| Target/group | Includes |
|---|---|
default |
All images defined in docker-bake.hcl |
stack |
base, sound-base, core |
services |
audio, cli, core, gui-websocket, listener, messagebus, phal, phal-admin |
skills |
skill-base plus one skill-<name> image per entry of the SKILLS list in docker-bake.hcl |
Adding a skill is one entry in SKILLS plus a skills/skill-<name>/Dockerfile;
the bake target, tags and cache settings are generated from the list.
Build a single target with -T, for example:
Variables¶
Defaults come from scripts/bake.sh and docker-bake.hcl:
| Variable | Default | Description |
|---|---|---|
REGISTRY |
docker.io/smartgic |
Registry prefix for tags |
TAG |
alpha |
Image tag to publish |
LATEST_TAG |
latest |
Additional tag applied only when TAG=stable |
VERSION |
alpha |
Version label passed into images |
CHANNEL |
alpha |
Constraints channel (alpha, testing, stable) |
OVOS_RELEASES_REF |
main |
Git ref of ovos-releases the constraints-${CHANNEL}.txt file is taken from; a commit SHA makes the build reproducible |
UV_PRERELEASE |
allow |
uv pip prerelease policy; use if-necessary-or-explicit for testing/stable (CI does) |
CACHE_REPO |
ghcr.io/openvoiceos/ovos-docker-cache |
Registry build cache, read anonymously |
CACHE_TO |
(empty) | max also exports the build cache (needs GHCR write access; CI does this) |
MIRROR_REGISTRY |
(empty) | Second registry every image is also pushed to (CI: ghcr.io/openvoiceos/ovos-docker); the pipeline reads manifests, labels and SBOMs from it |
PLATFORMS |
linux/amd64,linux/arm64 |
Platforms to build |
TARGETS |
default |
Bake targets/groups |
PUSH |
true |
Push images to the registry |
LOAD |
false |
Load images locally (forces linux/amd64) |
CACHE_FROM |
true |
Enable registry cache-from |
ENSURE_BINFMT |
auto |
auto, true, or false binfmt/qemu installation |
BUILDER |
ovos-bake |
Buildx builder name for multi-arch builds |
BUILD_DATE and GIT_SHA are set automatically by scripts/bake.sh.
Build args and constraints¶
The Dockerfiles use a few build args that are set via Bake:
VERSIONsets image labels and is exposed in runtime metadata.CHANNEL/OVOS_CHANNELselects the constraints file fromovos-releases(for example,constraints-alpha.txt).UV_PRERELEASEcontrols pre-release resolution for images that install packages viauv pip.constraints-alpha.txtpins pre-releases;constraints-testing.txtandconstraints-stable.txtare ranges of stable versions, so they must be built withif-necessary-or-explicit.OVOS_RELEASES_REFpins the constraints file to a commit ofovos-releases. The file is fetched withADD, so BuildKit rebuilds the install layer exactly when the constraints change, and every image records the commit in theio.openvoiceos.constraints.reflabel.
Notes¶
--loadand--pushare mutually exclusive.--loadforceslinux/amd64because Docker cannot load multi-arch manifests locally.- When
TAG=stable, Bake also tagsLATEST_TAG(defaultlatest). - For multi-arch builds,
scripts/bake.shinstalls binfmt/qemu automatically when needed (setENSURE_BINFMT=trueor pass--ensure-binfmtto force, orENSURE_BINFMT=false/--no-binfmtto skip). - The script switches to a
docker-containerbuildx builder for multi-arch builds (override withBUILDERor--builder). - Use
--no-cache-fromif registry cache is unavailable or not desired.
Continuous integration¶
Published images are built by GitHub Actions, not by hand:
| Workflow | Trigger | Builds |
|---|---|---|
on-push.yml |
commit on dev |
the targets whose build context changed, plus everything built on top of them, for every channel |
on-constraints.yml |
ovos-releases constraints change (dispatch or hourly poll) |
per channel, only the images that contain a package whose constraint line changed |
pull-request.yml |
pull request | the affected targets on both architectures, without pushing |
scheduled-rebuild.yml |
weekly | every image of a channel, so base-OS fixes reach the images |
build-images.yml |
called by the above, or manually | the reusable build: resolve → build per architecture on native runners → verify → merge |
Each architecture is built natively (ubuntu-24.04, ubuntu-24.04-arm) and
pushed as <image>:<channel>-<arch>. The channel tag (<image>:<channel>,
plus latest for stable, plus an immutable <channel>-YYYYMMDD tag) is a
manifest list created only after both architectures were verified, and it is
signed with cosign (keyless). A failed
architecture therefore never moves the tag the installer pulls.
The build-state branch records what each channel was built from (digest,
ovos-releases commit, installed packages) so the poll can decide what to
rebuild without touching the registry.