Running commands in the IC dev container
./ci/container/container-run.sh runs a command inside the pinned IC dev
container (the ghcr.io/dfinity/ic-dev image), bind-mounting the repo checkout
at the same canonical (symlink-resolved) absolute path it has on the host,
which is also the working directory, and reusing ~/.cache for the Bazel/cargo
caches.
Prefer running builds and build tooling through it: it gives you the exact, pinned toolchain environment, and standardizing on the container — regardless of whether it's backed by podman or docker — keeps things simple and consistent.
Choosing the container runtime
The script supports two runtimes, selected by the CONTAINER_RUNTIME env var:
- podman (default) — rootful and privileged.
- docker — for hosts that have the Docker daemon but no podman.
Which one to use depends on where you are:
- namespace.so devboxes (e.g. this machine —
test -d /.namespace): use docker. podman isn't available there, and the namespace.so daemon can't do some things podman's setup expects (e.g. bind-mounting the host's/tmp). - DFINITY infra, in particular a "devenv" machine (which
container-run.shdetects via/var/lib/cloud/instanceplus a/hoststoragemount): use podman — the default, so no env var needed.
On a docker host, prefix every invocation with CONTAINER_RUNTIME=docker:
# interactive shell in the container
CONTAINER_RUNTIME=docker ./ci/container/container-run.sh
# run a single command and exit
CONTAINER_RUNTIME=docker ./ci/container/container-run.sh <command> [args...]
If CONTAINER_RUNTIME is unsupported the script errors out early; if the chosen
runtime's daemon isn't reachable it prints which command it tried.
Notes
- The repo is mounted at its canonical host path (
git rev-parse --show-toplevel, i.e. with symlinks resolved) and that's the working directory, so both repo-relative paths and canonical absolute host paths of files in the checkout work inside the container, e.g.CONTAINER_RUNTIME=docker ./ci/container/container-run.sh ./path/to/script.sh. Linked git worktrees work too: run the script from the worktree directory. - The image is pulled from
ghcr.ioon first use (large, one-time). - Anything the command writes into the checkout (or
~/.cache) persists on the host, since those are bind-mounted. Each checkout gets its own bazel output base; the install base and repository cache are shared. - Don't nest: the script refuses to run inside an existing container.
Keeping a namespace.so devbox awake for long-running work
On a namespace.so devbox (test -d /.namespace), the machine can go to sleep
during a long unattended operation — a multi-minute bazel build/bazel test, a container image build/pull, etc. — killing it partway through.
Before starting long-running work, create the marker file:
mkdir -p /.namespace/tasks && touch /.namespace/tasks/stay-live
and remove it once the work is done, so the devbox is allowed to sleep again:
rm -f /.namespace/tasks/stay-live
This is independent of whether the work itself runs inside the dev container or directly on the host.