Container Image Build
Wanaku provides first-class support for building container images through the Cargo toolchain. Use cargo build-image or make build-image to build the image locally, or target a specific runtime environment with the --target flag.
Prerequisites
Install the tool that matches your target environment.
| Target | Required tool | Install reference |
|---|---|---|
docker (default) | docker (falls back to podman) | Docker / Podman |
minikube | docker + minikube | Minikube |
openshift | podman + oc CLI, logged in to a cluster | Podman / OpenShift CLI |
Commands
cargo build-image
Build the full image (includes the embedded admin UI).
cargo build-image # build only
cargo build-image --push # build and push to the registryThe tag is always suffixed with the host architecture, for example:
quay.io/wanaku/wanaku-server:latest-x86_64on an x86_64 machinequay.io/wanaku/wanaku-server:latest-aarch64on a Linux ARM64 machine
Pass --push to push the image to the registry after the build. Without --push, the image is built locally and the command exits. Exception: the minikube target loads the image into the cluster daemon directly with the plain tag, and does not push regardless of --push.
cargo build-image-headless
Build the headless image (no embedded admin UI).
cargo build-image-headless # build only
cargo build-image-headless --push # build and push to the registryThis is a shorthand for cargo build-image --variant headless. The same arch-suffix and --push behaviour applies.
cargo build-image-manifest
Assemble a multi-arch manifest list from existing arch-specific images.
cargo build-image-manifest --tag quay.io/wanaku/wanaku-server:myreleaseThis command requires that both <tag>-aarch64 and <tag>-x86_64 are already in the registry. It assembles them into a manifest list at <tag> using podman manifest create + podman manifest push --all.
podman must be on PATH. The minikube target is not supported for manifest assembly.
Custom tag
Use --tag to set a custom image tag. Defaults to quay.io/wanaku/wanaku-server:latest.
cargo build-image --push --tag registry.example.com/myorg/wanaku-server:v1.2.3
# pushes: registry.example.com/myorg/wanaku-server:v1.2.3-x86_64 (on x86_64)For the openshift target, the tag suffix (the part after the last :) is extracted and used as the tag on the OpenShift registry push. For example, --tag ...:v1.2.3 pushes as <registry>/<namespace>/wanaku-server:v1.2.3-<arch>.
Multi-arch builds
Multi-arch support uses the native two-pass approach — no QEMU emulation. Each machine builds only its own native architecture.
Step 1 — build and push the arch-specific image on each machine:
# On an x86_64 machine:
cargo build-image --push --tag quay.io/wanaku/wanaku-server:myrelease
# pushes: quay.io/wanaku/wanaku-server:myrelease-x86_64
# On an aarch64 machine:
cargo build-image --push --tag quay.io/wanaku/wanaku-server:myrelease
# pushes: quay.io/wanaku/wanaku-server:myrelease-aarch64Step 2 — assemble the manifest list (run once, on either machine, after both arch images are in the registry):
cargo build-image-manifest --tag quay.io/wanaku/wanaku-server:myrelease
# creates manifest list: quay.io/wanaku/wanaku-server:myrelease → aarch64 + x86_64The headless variant follows the same pattern:
cargo build-image-headless --push --tag quay.io/wanaku/wanaku-server-headless:myrelease
cargo build-image-manifest --variant headless --tag quay.io/wanaku/wanaku-server-headless:myreleaseRelationship to CI
The main-build.yml CI workflow runs on a matrix of ubuntu-latest (x86_64) and ubuntu-24.04-arm (aarch64) runners. Each runner calls cargo build-image and pushes its arch-specific image. A separate createManifests job then calls cargo build-image-manifest to assemble the final manifest list.
Builds from the main branch use the tag latest. Their arch-specific tags are latest-x86_64 and latest-aarch64. The manifest list uses latest.
Builds from other branches use the branch name as the tag. Their arch-specific tags use the branch name with -x86_64 or -aarch64. The manifest list uses the branch name.
--target flag
Pass --target to select the build environment. The default is docker.
cargo build-image --target docker # default
cargo build-image --target minikube
cargo build-image --target openshiftBoth build-image and build-image-headless accept --target.
cargo build-image-headless --target minikubeTarget details
docker (default)
Runs the following command using docker (or podman if docker is not on PATH).
docker build -f Containerfile --build-arg VARIANT=full -t <tag>-<arch> .
docker push <tag>-<arch> # only when --push is givenWhen --push is given, the command pushes the image and prints the next steps for multi-arch manifest assembly. Without --push, the build stops after the local image is created.
minikube
Loads the image directly into Minikube's internal image store. No registry push is required. The xtask runs minikube docker-env --shell none, parses the returned environment variables, and injects them into the docker build subprocess. You do not need to run eval $(minikube docker-env) manually.
The Minikube cluster must be running before you invoke this command.
minikube start # if not already running
cargo build-image --target minikubeThe plain tag (no arch suffix) is used so the image is immediately available to pods.
kubectl run wanaku --image=quay.io/wanaku/wanaku-server:latest --image-pull-policy=NeverNote:
cargo build-image-manifestdoes not support theminikubetarget. Minikube's internal daemon serves a single-arch cluster; a multi-arch manifest is not meaningful there.
openshift
Builds the image locally with Podman, then pushes the final runtime image to the OpenShift internal registry. The Rust compilation runs on your machine, not on the cluster.
Why not
oc start-build --from-dir?S2I binary builds have two fundamental problems for this project:
- Ephemeral storage: the Containerfile performs a full Rust compilation inside the build pod. It needs >10 GB of ephemeral storage. OpenShift cluster nodes typically enforce a 5–6 GB limit, so the pod is evicted before the build finishes.
- Build args ignored:
oc start-build --from-dirsilently drops--build-arg. TheVARIANTarg is never forwarded to the Containerfile.The correct approach: build locally with Podman (cargo caches exist on your machine), then push only the final ~100 MB runtime image to the registry.
What the xtask does
- Checks that you are logged in with
oc whoami. - Resolves the internal registry hostname via
oc get route/default-route -n openshift-image-registry. - Resolves the current namespace with
oc project -q. - Obtains the auth token with
oc whoami -t. - Logs
podmaninto the registry (podman login --tls-verify=false). - Builds the image locally with
podman, tagging it with both the local--tagand the registry reference<registry>/<namespace>/wanaku-server:<tag-suffix>-<arch>. - Pushes only the final runtime image to the OpenShift registry (
podman push --tls-verify=false). - Prints the full image reference for use in a
Deploymentoroc new-app.
Podman is required (not Docker) because it supports --tls-verify=false on both login and push, which is needed when the internal registry uses a self-signed certificate.
Usage
You must be logged in and have the internal registry exposed.
# Expose the internal registry (one-time cluster admin step)
oc patch configs.imageregistry.operator.openshift.io/cluster \
--patch '{"spec":{"defaultRoute":true}}' --type=merge
# Log in, build, and push
oc login <cluster-url>
cargo build-image --target openshift --pushThe xtask prints the image reference at the end:
Registry reference : default-route-openshift-image-registry.apps.example.com/myproject/wanaku-server:latest-x86_64To push a specific version tag:
cargo build-image --target openshift --push --tag quay.io/wanaku/wanaku-server:v0.3.0
# pushes as: <registry>/<namespace>/wanaku-server:v0.3.0-<arch>To assemble a multi-arch manifest after both arch images are pushed:
cargo build-image-manifest --target openshift --tag quay.io/wanaku/wanaku-server:v0.3.0Make targets
Both commands are also available as Make targets. Use the TARGET variable to select the environment.
make build-image # docker, full variant (build only)
make build-image TARGET=minikube # minikube, full variant
make build-image TARGET=openshift # openshift, full variant (build only)
make build-image-headless # docker, headless variant (build only)
make build-image-headless TARGET=minikube