> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/infra-controller/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/infra-controller/_mcp/server.

# Building NICo Containers

This section provides instructions for building the containers for NVIDIA Infra Controller (NICo).

## Installing Prerequisite Software

An Ubuntu 24.04 host or VM with at least 150GB of free disk space is required, and git and make must also be installed (macOS is not supported).

Clone the repo and run the build-host bootstrap. It installs everything needed to build
the containers and boot artifacts -- system packages, rustup, the mkosi/ipxe git
submodules, Docker, and the cargo build tooling -- in one idempotent step:

```sh
git clone git@github.com:NVIDIA/infra-controller.git
cd infra-controller
make bootstrap          # or: ./scripts/setup-build-host.sh
```

Reboot (or log out and back in) afterwards so the `docker` group membership and the
userns sysctl change take effect.

### Manual setup (what `make bootstrap` does)

`make bootstrap` runs `scripts/setup-build-host.sh`, which is equivalent to the following
steps on an `apt`-based distribution such as Ubuntu 24.04:

1. `apt-get install build-essential cpio direnv mkosi uidmap curl file fakeroot git docker.io docker-buildx sccache protobuf-compiler libopenipmi-dev libudev-dev libboost-dev libgrpc-dev libprotobuf-dev libssl-dev libtss2-dev kea-dev systemd-boot systemd-ukify jq zip`
2. [Add the correct hook for your shell](https://direnv.net/docs/hook.html)
3. Install rustup: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` (select Option 1)
4. Start a new shell to pick up changes made from direnv and rustup.
5. Clone NICo - `git clone git@github.com:NVIDIA/infra-controller.git infra-controller`
6. `cd infra-controller`
7. `direnv allow`
8. `git submodule update --init --recursive`
9. `sudo systemctl enable docker.socket`
10. `cargo install cargo-make cargo-cache`
11. `echo "kernel.apparmor_restrict_unprivileged_userns=0" | sudo tee /etc/sysctl.d/99-userns.conf`
12. `sudo usermod -aG docker $(id -un)`
13. `reboot`


## Build all images with one command

Once the prerequisites above are installed, build the NICo container images from the
top of the repo with a single `make` command:

```sh
make images          # deployable stack: NICo Core (nico) + the REST service images
make images-all      # the above plus the machine-validation and x86 boot-artifact images
```

Images are tagged `localhost:5000/<name>:latest` by default. Override the registry and
tag to build under your own registry:

```sh
make images IMAGE_REGISTRY=my-registry.example.com/nico IMAGE_TAG=v1.0.0
```

The deployable images are built for `linux/amd64` (the NICo Dockerfiles are x86_64).
On an arm64 host such as Apple Silicon they build under emulation, which is slow — a
native `linux/amd64` build host is recommended. Pass `PLATFORM=linux/arm64` to build
native arm64 images instead.

Run `make help` from the repo root to list the individual image targets (`images-core`,
`images-rest`, `images-machine-validation`, `images-boot-artifacts`, `images-bfb`). The
sections below document the per-image build commands that these targets wrap, for when you
need to build or debug a single image.

### Verifying the build

After `make images-all` completes, verify that all 14 deployable images were produced:

```sh
docker images --filter "reference=${IMAGE_REGISTRY}/*:${IMAGE_TAG}" \
  --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
```

The count should be exactly 14:

| Image | Target |
|---|---|
| `nico` | `images-core` |
| `nico-rest-api` | `images-rest` |
| `nico-rest-workflow` | `images-rest` |
| `nico-rest-site-manager` | `images-rest` |
| `nico-rest-site-agent` | `images-rest` |
| `nico-rest-db` | `images-rest` |
| `nico-rest-cert-manager` | `images-rest` |
| `nico-flow` | `images-rest` |
| `nico-psm` | `images-rest` |
| `nico-nsm` | `images-rest` |
| `nico-mcp` | `images-rest` |
| `machine-validation` | `images-machine-validation` |
| `boot-artifacts-x86_64` | `images-boot-artifacts` |
| `boot-artifacts-aarch64` | `images-bfb` |

```sh
# Quick count — should print 14
docker images --filter "reference=${IMAGE_REGISTRY}/*:${IMAGE_TAG}" \
  --format "{{.Repository}}" | wc -l
```

If the count is less than 14, the missing images indicate which sub-target failed. The three boot/validation images (`machine-validation`, `boot-artifacts-x86_64`, `boot-artifacts-aarch64`) require the full mkosi + Rust toolchain — if only 11 images appear, use `make images` instead of `make images-all` to build the deployable stack without them.

Three intermediate images are also created locally but are not tagged under `IMAGE_REGISTRY`: `nico-buildcontainer-x86_64`, `nico-runtime-container-x86_64`, and `machine-validation-runner`. These are build-time dependencies only and do not need to be pushed.

## Building X86_64 Containers

**NOTE**: Execute these tasks in order. All commands are run from the top of the `infra-controller` directory.

### Building the X86 build container

```sh
docker build --file dev/docker/Dockerfile.build-container-x86_64 -t nico-buildcontainer-x86_64 .
```

### Building the X86 runtime container

```sh
docker build --file dev/docker/Dockerfile.runtime-container-x86_64 -t nico-runtime-container-x86_64 .
```

### Building the boot artifact containers

```sh
cargo make --cwd pxe --env SA_ENABLEMENT=1 build-boot-artifacts-x86-host-sa
docker build --build-arg "CONTAINER_RUNTIME_X86_64=alpine:latest" -t boot-artifacts-x86_64 -f dev/docker/Dockerfile.release-artifacts-x86_64 .
```

## Building the Machine Validation images

```sh
docker build --build-arg CONTAINER_RUNTIME_X86_64=nico-runtime-container-x86_64 -t machine-validation-runner -f dev/docker/Dockerfile.machine-validation-runner .

docker save --output crates/machine-validation/images/machine-validation-runner.tar machine-validation-runner:latest 

// This copies `machine-validation-runner.tar` into the `/images` directory on the `machine-validation-config` container.  When using a kubernetes deployment model
// this is the only `machine-validation` container you need to configure on the `nico-pxe` pod.

docker build --build-arg CONTAINER_RUNTIME_X86_64=nico-runtime-container-x86_64 -t machine-validation-config -f dev/docker/Dockerfile.machine-validation-config .

```

## Building nico-core container

```sh
docker build --build-arg "CONTAINER_RUNTIME_X86_64=nico-runtime-container-x86_64" --build-arg "CONTAINER_BUILD_X86_64=nico-buildcontainer-x86_64" -f dev/docker/Dockerfile.release-container-sa-x86_64 -t nico .
```

## Building the AARCH64 Containers and artifacts

### Building the Cross-compile container

```sh
docker build --file dev/docker/Dockerfile.build-artifacts-container-cross-aarch64 -t build-artifacts-container-cross-aarch64 .
```

## Building the admin-cli
The `admin-cli` build does not produce a container. It produces a binary:

`$REPO_ROOT/target/release/nico-admin-cli`

```
BUILD_CONTAINER_X86_URL="nico-buildcontainer-x86_64" cargo make build-cli
```

### Building the DPU BFB

```sh
cargo make --cwd pxe --env SA_ENABLEMENT=1 build-boot-artifacts-bfb-sa

docker build --build-arg "CONTAINER_RUNTIME_AARCH64=alpine:latest" -t boot-artifacts-aarch64 -f dev/docker/Dockerfile.release-artifacts-aarch64 .
```

**NOTE**: The `CONTAINER_RUNTIME_AARCH64=alpine:latest` build argument must be included. The aarch64 binaries are bundled into an x86 container.