Skip to content

Install on Talos under Incus

Install one digest-pinned static pod in each Talos virtual machine that needs the Incus guest agent.

Prerequisites

You need:

  • an Incus virtual machine running Talos Linux;
  • Incus host access that can change the instance configuration;
  • talosctl access to the target Talos node;
  • Docker Buildx to resolve the release image digest;
  • a local checkout of this repository for the canonical patch template; and
  • public, anonymous read access to ghcr.io/componere/incus-guest-agent.

The supported deployment is the machine.pods static pod in deploy/talos/incus-guest-agent.yaml.tmpl. The production acceptance test used Incus 7.3 and Talos 1.13.9; other versions are untested.

The canonical patch has no imagePullSecrets, so the GHCR package must stay publicly readable. Before changing Talos, run the digest command below from a workstation that has no GHCR credentials configured. Success confirms the package resolves anonymously; the node still needs its own network access to ghcr.io when it pulls the image.

Attach the Incus configuration media

Run this command on the Incus host:

INSTANCE=talos-node-1
incus config device add "$INSTANCE" agent disk source=agent:config

If the agent device already exists, leave it attached. Inspect the current devices with:

incus config device show "$INSTANCE"

The wrapper can start before the media appears. It checks /dev/sr* every two seconds and starts the host-supplied agent after valid media is available.

Resolve an immutable image digest

Choose a stable release number without the leading v, resolve its OCI index digest, and render the canonical patch:

IMAGE=ghcr.io/componere/incus-guest-agent
VERSION=1.2.3
DIGEST="$(docker buildx imagetools inspect \
  "$IMAGE:$VERSION" --format '{{.Manifest.Digest}}')"

case "$DIGEST" in
  sha256:*) ;;
  *) echo "unexpected image digest: $DIGEST" >&2; exit 1 ;;
esac

sed "s|sha256:<digest>|$DIGEST|" \
  deploy/talos/incus-guest-agent.yaml.tmpl \
  > incus-guest-agent.yaml

Keep the rendered patch and digest in your deployment records. The mutable release tag is only a lookup input; the Talos configuration must contain the resolved sha256: digest.

Apply the patch to one node

Set the Talos API address and apply the rendered patch without rebooting:

NODE=10.0.0.10
TALOSCONFIG=./talosconfig

talosctl --talosconfig "$TALOSCONFIG" --nodes "$NODE" \
  patch machineconfig --mode no-reboot \
  --patch @incus-guest-agent.yaml

Repeat this operation one node at a time. Verify each node before changing the next node.

Verify the wrapper and host-supplied agent

List the Kubernetes containers through the Talos API:

talosctl --talosconfig "$TALOSCONFIG" --nodes "$NODE" \
  containers --kubernetes

The output must contain a running kube-system/incus-guest-agent-<node name>:agent container.

Read the wrapper log without using the Kubernetes API. Set NODE_NAME to the Talos node name:

NODE_NAME=talos-node-1

talosctl --talosconfig "$TALOSCONFIG" --nodes "$NODE" \
  logs --kubernetes --tail 50 \
  "kube-system/incus-guest-agent-${NODE_NAME}:agent"

A successful start logs these messages in order (the device value names the accepted optical device and can differ):

waiting for Incus configuration media
staged Incus agent files device=/dev/sr0
starting host-supplied incus-agent

On the Incus host, confirm that the host can query and execute through the guest agent:

incus info "$INSTANCE"
incus exec "$INSTANCE" -- /usr/bin/incus-guest-agent --version

incus info must include live guest OS and address data. The version command must print incus-guest-agent <version> for the installed release.

Verify a consumer of /dev/incus/sock

Set a fresh value on the Incus host:

NONCE="incus-agent-check-$(date +%s)"
incus config set "$INSTANCE" user.incus-agent-check "$NONCE"

From the static-pod workload that consumes Incus configuration, query the mounted socket:

curl --fail --silent --show-error \
  --unix-socket /dev/incus/sock \
  http://incus/1.0/config/user.incus-agent-check

The response must equal $NONCE. The consumer must mount the host's /dev/incus/sock; the production acceptance test used a privileged static pod with the host /dev mounted at /dev.

Remove the test value after verification:

incus config unset "$INSTANCE" user.incus-agent-check

For the required pod security profile and failure interpretation, see Runtime and operations and Why the static pod is privileged.