incus-compose pull is the only command that needs a registry. Everything after
it - up, down, start, stop, restart, exec, run, backup - works
against the local image store, so a project can be pulled on a connected machine
and run on a disconnected one.
Three images, not one. Two of them are easy to forget, because nothing in the compose file names them:
| Image | Comes from | Point it elsewhere with |
|---|---|---|
| Service images | image: in the compose file |
an Incus remote, see Mirrors |
| ic-healthd sidecar | ghcr.io/lxc/incus-compose/ic-healthd |
--healthd-image, INCUS_COMPOSE_HEALTHD_IMAGE |
The run helper |
ghcr.io/lxc/incus-compose/ic-sleep |
--init, x-incus-compose.init, INCUS_COMPOSE_INIT_IMAGE |
pull fetches all three, but only warns on the last two - see
How the exit code is obtained.
That warning is the only notice you get that incus-compose run will fail
later, disconnected, so do not let it scroll past.
incus-compose pull
That is the whole connected step. --policy defaults to always, so it
refreshes what it already has.
Pulling is also what captures each image's
entrypoint/command split,
the one thing incus-compose reads from a registry directly. Project copies carry
it, so command: still replaces CMD rather than the whole argv once you are
offline.
The images land in the shared cache project (incus-compose-cache by default),
which survives down and up. Do not set --image-cache "" on a machine that
has to work disconnected: that skips the cache project, and the images then only
exist as per-project copies that down removes.
incus-compose up --pull never
never means a store hit wins and a store miss is a hard failure - the registry
is never contacted. Without it the default is missing, which is silent about
the difference until the moment something is absent and the pull hangs on a
registry that is not there.
The spelling differs by command, which is worth pinning in scripts:
| Command | Flag | Values | Default |
|---|---|---|---|
up |
--pull |
always, missing, never, policy |
policy |
pull |
--policy |
always, missing, never |
always |
run |
--pull |
always, missing, never |
missing |
build |
--pull |
always, missing, never, policy |
policy |
Set it once for the whole machine instead of per invocation:
export INCUS_COMPOSE_UP_PULL=never
export INCUS_COMPOSE_RUN_PULL=never
Most "air-gapped" networks are really proxied: no route to Docker Hub, but a
registry of your own. Adding an Incus remote for one of the six built-in
registries with incus remote add overrides its built-in address, so
image: nginx:alpine still resolves to docker.io/library/nginx:alpine and the
compose file does not change. See
Images for the commands, resolution
and registry authentication, and
OCI Registry Cache for running the pull-through
caches themselves on Incus.
The two incus-compose images need the same treatment, since ghcr.io is a
different upstream:
export INCUS_COMPOSE_HEALTHD_IMAGE=registry.example.com/incus-compose/ic-healthd:{version}
export INCUS_COMPOSE_INIT_IMAGE=registry.example.com/incus-compose/ic-sleep:{version}
{version} is replaced with the incus-compose version, so one value survives an
upgrade.
build: services still need whatever their Dockerfile pulls. The builder
is Podman or Docker and its base-image freshness is its own concern - see
Builds.incus-compose run - see
How the exit code is obtained.self-update reaches GitHub by definition.Dockerfile HEALTHCHECK is not read, and one reason is this page: fetching it
would mean registry access on every up. Declare healthcheck.test in the
compose file instead - see
Dockerfile HEALTHCHECK Not Supported.