# Containerization > **NOTE**: image tag base (`azaion/missions`), csproj name (`Azaion.Missions`), and Dockerfile ENTRYPOINT (`dotnet Azaion.Missions.dll`) reflect the post-rename state. Renames landed under Jira AZ-544 (B5 — csproj/namespace) and AZ-549 (B10 — Woodpecker image tag + suite compose). ## Source `./Dockerfile` (single Dockerfile at the repo root). Multi-arch via build args. ## Build strategy: multi-arch from a single Dockerfile ```dockerfile FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS build ARG TARGETARCH WORKDIR /src COPY . . RUN arch=$([ "$TARGETARCH" = "amd64" ] && echo "x64" || echo "$TARGETARCH") && \ dotnet publish -c Release -o /app --os linux --arch $arch FROM mcr.microsoft.com/dotnet/aspnet:10.0 ARG CI_COMMIT_SHA=unknown ENV AZAION_REVISION=$CI_COMMIT_SHA WORKDIR /app COPY --from=build /app . EXPOSE 8080 ENTRYPOINT ["dotnet", "Azaion.Missions.dll"] # post-B5 + B10 ``` Key choices: - **`--platform=$BUILDPLATFORM`** on the build stage — the SDK runs on the **builder's** native architecture (typically AMD64 in CI), but `dotnet publish --os linux --arch $arch` cross-publishes for the **target** architecture (`arm64` for Jetson / OPi; `amd64` for operator-PC). This avoids needing QEMU emulation for the (slow) build phase. - **Two-stage**: SDK image (~800 MB) for the build, runtime image (`mcr.microsoft.com/dotnet/aspnet:10.0`, ~210 MB) for the published artifacts. Final image carries no SDK, no source code, no `.csproj`. - **`AZAION_REVISION` env var** baked from `CI_COMMIT_SHA` at build time — surfaces the source commit at runtime for support / triage. Not consumed by the application code today; visible only via `docker inspect` or `env` in the running container. - **`EXPOSE 8080`** matches the ASP.NET Core default (no `ASPNETCORE_URLS` override) and the edge compose port mapping `5002:8080`. ## What the Dockerfile does **NOT** do (carry-forward improvements) - **No `.dockerignore`** — every file under the repo root is copied into the build context, including `_docs/`, `.cursor/`, and the previously-committed `obj/` / `bin/` (cleaned by `dotnet publish` but still copied across the wire). A `.dockerignore` would shrink the build context meaningfully on Jetson-class builders. Tracked as opportunistic improvement. - **No `HEALTHCHECK` directive** — `/health` exists in the application (Flow F7) but the container itself does not declare a healthcheck. Compose-level healthcheck is the suite's expected mechanism. - **No non-root `USER`** — the runtime image runs as root. The `aspnet:10.0` base image supports a non-root user (`USER app`) since .NET 8; switching is a one-line change on the next refresh and would tighten container isolation. - **No build-time test pass** — no `dotnet test` step. The repo has no test project today (tracked in `../../../suite/_docs/_process_leftovers/2026-04-22_ci-unit-test-lane-missing-projects.md`); when a `tests/Azaion.Missions.Tests/` project lands, a pre-publish `dotnet test` step is the natural next addition. ## Image lifecycle on edge devices 1. Woodpecker builds + pushes `${REGISTRY_HOST}/azaion/missions:${BRANCH}-arm` (post-B10) on every push to `dev` / `stage` / `main` (see `ci_cd_pipeline.md`). 2. Watchtower running on each edge device polls the registry; on a new digest for the device's pinned tag, it pulls and re-creates the container. 3. `flight-gate` (per `../../../suite/_docs/00_top_level_architecture.md`) prevents container restart mid-mission. Once the active mission completes, the new image becomes live. 4. Container starts → `Program.cs` → migrator → `app.Run()` (Flow F6). ## Image variants and tag strategy | Branch | Tag (post-B10) | Audience | |--------|----------------|----------| | `dev` | `azaion/missions:dev-arm` | Engineers; rolling latest-dev | | `stage` | `azaion/missions:stage-arm` | Pre-prod customer demo devices | | `main` | `azaion/missions:main-arm` | Production edge fleets | **No semantic version tags today** (no `v1.2.3`-style tags). Watchtower polls the named tags directly. Carry-forward improvement: add `${REGISTRY_HOST}/azaion/missions:${CI_COMMIT_SHA:0:8}-arm` alongside the branch tag so rollback to a specific build is possible without rebuilding. ## Multi-arch — current limitation Today the Woodpecker pipeline (`ci_cd_pipeline.md`) builds **only the `arm64` variant** (the runner is ARM64-labeled and the tag suffix is `-arm`). For AMD64 (operator-PC) deployments, a second pipeline / second runner / `linux/amd64` build args would be needed. Out of this Epic — the current operator-PC deployment story uses local builds.