Build appliance (nornir-build)
The production box that runs nornir-build against volumes on the NAS: compute (Ubuntu,
GPU), or a Windows workstation with Docker Desktop and WSL2. It is not the programmer shell and
not the Cursor worker (Programmer shell and Cursor worker); keep the roles separate.
Role |
Image |
Launcher |
|---|---|---|
Build appliance (GPU) |
|
|
Build appliance (CPU) |
|
same |
Live packages (rare) |
|
|
Images come from GHCR; the appliance does not build them. Image tags and how they are built are in Image catalogue.
One-time setup
Ubuntu (compute)
Steps 2 to 4 of Deployment and configuration cover this host. In short:
sudo ubuntu-drivers install && sudo reboot
sudo bash bootstrap-ubuntu.sh --gpu # as the deploy account: Docker, pwsh, cifs-utils, NVIDIA toolkit
# re-login, clone /srv/src/nornir and /srv/src/Viking
pwsh /srv/src/nornir/nornir-docker/deploy/Initialize-DockerHost.ps1 -Role compute -VikingRepoRoot /srv/src/Viking -SetUserEnv
echo cifs | sudo tee /etc/modules-load.d/nornir-cifs.conf
sudo modprobe cifs && grep cifs /proc/filesystems
Then edit the site files (next section) and pull the images:
docker login ghcr.io -u <github-user> # token: Packages read
stack nornir-build pull
Windows (Docker Desktop + WSL2)
Install Docker Desktop with the WSL2 backend, Git, PowerShell 7, and (for
nornir:cupy) a current NVIDIA driver.Clone the monorepo with submodules and run, from the checkout:
.\nornir-docker\deploy\Initialize-DockerHost.ps1 -Role compute -SetUserEnv
Load the CIFS driver in the WSL2 kernel (shared by Docker Desktop’s containers). In the WSL distro, with
systemd=trueunder[boot]in/etc/wsl.conf:sudo cp /mnt/d/src/git/nornir/nornir-docker/dev/example.modules-load.net-shares.conf /etc/modules-load.d/net-shares.conf sudo modprobe cifs grep cifs /proc/filesystems
Without systemd, add
command = /sbin/modprobe cifs nfsv4under[boot]instead. Runwsl --shutdownand check again.docker login ghcr.ioandD:\Docker\stack.ps1 nornir-build pull.
Every session
stack.ps1 nornir-build up -Target compute # long-lived container "nornir-build"
stack.ps1 nornir-build shell -Target compute # attach a bash shell
up creates Work/nornir-build-<version>-<stamp>/ and starts a detached container with that
folder at /workspace, the CIFS shares mounted, --shm-size=64g, nofile 65536, and
Logs/nornir-build at /var/log/nornir. shell attaches to it; if it is not running,
shell starts a throwaway interactive container (--rm) with a new session folder instead.
Without stack.ps1 the same launcher runs directly:
pwsh /srv/docker/Builds/nornir-build/start-nornir-build.ps1 (interactive, --rm); run that
way it does not read nornir-build.run.env, so export any overrides in the shell first.
A shell exec’d over SSH ends when the connection drops. For long builds, ssh to compute,
start tmux, and run stack nornir-build shell inside it.
Check the shell before starting a build:
echo "$NORNIR_NET_MOUNTS" # 1
findmnt -t cifs # the shares from nas-mounts.tsv
ls /storage4 | head
ulimit -n # 65536
nvidia-smi # nornir:cupy only
Interactive shells print a short nornir-build welcome and put TEMImport, TEMBuild,
TEMBuild-import and TEMAlign on PATH. Those are the lab build; what each stage does is
TEM build scripts.
GPU selection
With no -Image, start-nornir-build.ps1 runs Test-NornirGpu.ps1, which starts a short
nvidia-smi container with --gpus all:
success:
NORNIR_DOCKER_GPU=1, imagenornir:cupy,--gpus all;failure: image
nornir:prod, no GPU flags.
Force it with NORNIR_DOCKER_GPU=1 in nornir-build.run.env (or -Gpu), or turn it off
with -NoGpu. On Ubuntu the probe needs the NVIDIA container toolkit registered with Docker
(bootstrap-ubuntu.sh --gpu; stack.ps1 doctor checks it). Run the probe by hand with
pwsh nornir-docker/Test-NornirGpu.ps1.
Progress on the dashboard
Start nornir-dashboard first (Build dashboard (nornir-dashboard)). When NORNIR_MQTT_HOST is unset, the
launcher looks for the Docker network nornir-dashboard_default (NORNIR_MQTT_NETWORK); if
it exists, the container joins it and publishes to mosquitto:1883. This works on Linux, where
the broker is bound to 127.0.0.1 and is not reachable through host.docker.internal. If the
network does not exist, it falls back to host.docker.internal. NORNIR_MQTT_ENABLE=1 (the
template default) turns publishing on. Open the dashboard with stack.ps1 tunnel -Target compute.
Work sessions and pruning
Each up / interactive start gets its own Work/nornir-build-<version>-<stamp>/ (the old
layout used mounted-configs/). Builds write their output to the NAS, so these folders are
scratch:
stack.ps1 prune -Target compute # list folders older than 14 days
stack.ps1 prune -Days 30 -Force -Target compute
Folders mounted by a running container are never removed.
Refreshing images
stack.ps1 nornir-build pull -Target compute # docker-pull.ps1: nornir:prod + nornir:cupy
stack.ps1 nornir-build rebuild -Target compute # recreate the container on the new image
stack.ps1 nornir-dashboard rebuild -Target compute
docker-pull.ps1 can also run directly: -IncludeDashboard adds the dashboard image,
-PreferVersioned pins to the monorepo VERSION, -List shows which local tags differ
from GHCR, -ContinueOnError keeps going past one failed pull, -Help prints every flag. A
tag whose GHCR digest is already local is skipped.
Maintainer publish
On the machine that builds images (not part of the appliance checklist):
.\nornir-docker\docker-build.ps1 -List
.\nornir-docker\docker-build.ps1
.\nornir-docker\docker-push.ps1 -List
.\nornir-docker\docker-push.ps1
docker-push.ps1 with no image list pushes every catalogue image that is built locally and
whose GHCR digest differs; -Images cupy,gallery limits the set.
Problems: Troubleshooting.