Skip to main content

Custom Base Images

AgentVisor ships official host and guest images based on UBI 9 (Red Hat Universal Base Image). If your organization requires a different Linux distribution — for example Chainguard/Wolfi for provenance attestations, SBOM generation, or FIPS-validated packages — you can build replacement images as long as they satisfy the image contract described on this page.

When to use this

  • You need a specific distro for compliance reasons (FIPS, CIS hardening, SBOM attestation).
  • Your container registry policy prohibits UBI/RHEL base images.
  • You want to apply your own hardening or vulnerability scanning baseline.

Release artifacts

Starting with the first release that includes this feature, each GitHub release includes additional artifacts alongside the CLI binary. Find them at https://github.com/manetu/agentvisor/releases/tag/<VERSION>.

ArtifactDescription
agentvisor-host-runtime-linux-amd64-VERSION.tar.gzLicensed+obfuscated host-runtime (x86-64)
agentvisor-host-runtime-linux-arm64-VERSION.tar.gzLicensed+obfuscated host-runtime (ARM 64)
agentvisor-guest-runtime-linux-amd64-VERSION.tar.gzGuest-runtime binary (x86-64)
agentvisor-guest-runtime-linux-arm64-VERSION.tar.gzGuest-runtime binary (ARM 64)
agentvisor-<PYVER>-py3-none-any.whlPython SDK wheel (proto/gRPC code bundled)
checksums.txtSHA-256 sums for all of the above
Wheel naming

The Python wheel uses PEP 440 version normalization: the leading v is stripped and the first - (build-pipeline separator) is converted to +. For example, release tag v0.5.0 with pipeline ID 21 produces agentvisor-0.5.0+21-py3-none-any.whl. The recommended way to obtain the wheel filename is to read it directly from checksums.txt rather than constructing it — see the Quick start example below.

Signing

SHA-256 checksums are published with every release. Cosign signing via Sigstore is on the roadmap.

Compatibility guarantee

Always use the host-runtime and guest-runtime from the same release tag. Mixing versions across releases is unsupported — internal gRPC interfaces are not stable across release boundaries.

Download from GitHub Releases

Download tarballs and wheel files directly. Suitable for distroless or air-gapped builds.

VERSION=v1.2.3
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')

# Get checksums first
curl -fsSL \
https://github.com/manetu/agentvisor/releases/download/${VERSION}/checksums.txt \
-o checksums.txt

# Download and verify host-runtime
curl -fsSL \
https://github.com/manetu/agentvisor/releases/download/${VERSION}/agentvisor-host-runtime-linux-${ARCH}-${VERSION}.tar.gz \
-o agentvisor-host-runtime.tar.gz
sha256sum --check --ignore-missing checksums.txt # verify before use

Inside a Dockerfile, pass the checksums as build args and verify inline (see the reference Dockerfiles for a complete example).


Host image contract

Every custom host image must satisfy these requirements. They mirror what the official AgentVisor host image builds.

Binaries

PathDescription
/usr/local/bin/agentvisor-host-runtimeAgentVisor host binary (ENTRYPOINT). Path is fixed; not configurable.
/usr/local/bin/runscgVisor runsc binary
/usr/bin/newuidmapSUID helper for rootless user-namespace mapping
/usr/bin/newgidmapSUID helper for rootless user-namespace mapping
Preserve newuidmap/newgidmap SUID bits

When stripping SUID bits for hardening, exclude /usr/bin/newuidmap and /usr/bin/newgidmap. These SUID bits are required for rootless gVisor. All other SUID bits can be safely removed.

User and group

EntryUID/GIDPurpose
agentvisor groupGID 1000Shared group for socket directory access
agentvisor userUID 1000Runtime user; image runs as USER 1000
/etc/subuid entryagentvisor:100000:65536Sub-UID range for rootless namespace mapping
/etc/subgid entryagentvisor:100000:65536Sub-GID range for rootless namespace mapping

Directories

PathOwnerModePurpose
/var/run/agentvisoragentvisor:agentvisor0770Runtime state root — contains the runtime (gVisor state) and bundles (OCI bundle staging) subdirectories
/opt/guest-rootfs(none required)(none required)Mount point for the guest root filesystem layer; the official image leaves it root:root

Required environment variables

These must be set (either via ENV in the Dockerfile or at container startup):

VariableRequired value
AGENTVISOR_SOCKET_PATH/var/run/agentvisor/host.sock
AGENTVISOR_GUEST_ROOTFS_PATH/opt/guest-rootfs
AGENTVISOR_GUEST_RUNSC_PATH/usr/local/bin/runsc
AGENTVISOR_GUEST_RUNTIME_ROOT/var/run/agentvisor/runtime
AGENTVISOR_GUEST_BUNDLE_DIR/var/run/agentvisor/bundles
AGENTVISOR_GUEST_SANDBOXgvisor
HOME/home/agentvisor

Dockerfile boilerplate

USER 1000
ENTRYPOINT ["/usr/local/bin/agentvisor-host-runtime"]

Guest image contract

Every custom guest image must satisfy these requirements. They mirror what the official AgentVisor guest image builds.

Binary

PathDescription
/usr/local/bin/agentvisor-guest-runtimeAgentVisor guest binary (ENTRYPOINT). If your image places it elsewhere, set AGENTVISOR_GUEST_BINARY_PATH to that in-image path (gVisor sandbox only; Docker always uses the image ENTRYPOINT).

Users and groups

UserUIDPrimary GIDPurpose
agentvisor10001000Socket directory ownership; host-side identity in Docker transport
guest20001000 (agentvisor)Guest-runtime process after privilege drop; shares socket GID
agent200165534 (nobody)Python agent processes; cannot access the agentvisor socket

Home directories (/home/agentvisor, /home/guest, /home/agent) should be created if your build uses --create-home with useradd. The official UBI image uses --no-create-home, but the Chainguard reference image below uses --create-home for broader compatibility.

Directories

PathOwnerModePurpose
/app/agentagentvisor:agentvisor0770Agent source code (fixed path — required by the sandbox)
/app/agent/.agentvisoragentvisor:agentvisor0770Schema cache
/var/run/agentvisoragentvisor:agentvisor0770Socket directory
/app/agent is a fixed contract path

The path /app/agent is hardcoded in the host-to-guest sandbox specification. Custom guest images must place agent code at this path (or accept it being mounted there). This is analogous to how nginx always reads config from /etc/nginx/nginx.conf.

Environment variables

VariableValueNotes
PYTHONDONTWRITEBYTECODE1Recommended in image
Host socket is injected at runtime

AGENTVISOR_GUEST_HOST_SOCKET is set by the host at container launch time. You do not need to bake it into the image. Setting AGENTVISOR_HOST_SOCKET in a Dockerfile ENV has no effect — the variable read by the guest-runtime is AGENTVISOR_GUEST_HOST_SOCKET.

Python variant requirements

Required additionally when io.agentvisor.image.variant=python:

RequirementNotes
Python ≥ 3.10 reachable as python3Symlink at /usr/bin/python3 or /usr/local/bin/python3
agentvisor wheel installed system-wideFrom agentvisor-VERSION-py3-none-any.whl (proto/gRPC code is bundled — no separate proto wheel needed)
requests installedStandard HTTP library used by agent examples

OCI labels

The host-runtime reads these labels from the guest image at startup to auto-configure the interpreter and exec mode. Set them with LABEL directives in your Dockerfile.

LabelExampleDescription
io.agentvisor.typeguestAdvisory; logged at startup for observability
io.agentvisor.image.variantpython or minimalInformational; logged at startup
io.agentvisor.framework.providerlanggraph, crewai, adk, strands, interactiveSets the agent framework interpreter
io.agentvisor.exec-modefalseSet to true for interactive/exec-mode images
LABEL io.agentvisor.type=guest \
io.agentvisor.image.variant=python \
io.agentvisor.framework.provider=langgraph \
io.agentvisor.exec-mode=false

Dockerfile boilerplate

# No USER directive — guest-runtime starts as UID 0 for privilege separation,
# then drops to UID 2000 (guest) internally.
ENTRYPOINT ["/usr/local/bin/agentvisor-guest-runtime"]

Hardening recommendations

The official guest image applies defense-in-depth hardening after package installation. We recommend replicating the key steps in your custom image:

# Remove namespace/mount/privilege-escalation tools
RUN rm -f /usr/bin/unshare /usr/bin/nsenter /usr/bin/mount /usr/bin/umount \
/usr/sbin/chroot /usr/bin/newgrp /usr/bin/sg 2>/dev/null || true

# Strip SUID/SGID bits from all remaining binaries
RUN find / -xdev -type f \( -perm -4000 -o -perm -2000 \) \
-exec chmod u-s,g-s {} + 2>/dev/null || true

# Remove the package manager (prevents runtime package installation)
RUN apt-get remove --purge -y apt ... || apk del apk-tools ...

See the Security deployment page for the rationale behind each step.


Reference: Chainguard / Wolfi

The following reference Dockerfiles build replacements for the official AgentVisor images using Chainguard/Wolfi as the base OS. Save them to a local directory (e.g., agentvisor-chainguard/) before running the Quick start commands below.

Dockerfile.host
# AgentVisor Host Runtime — Chainguard / Wolfi reference image
#
# This Dockerfile builds a replacement for the official agentvisor-host image
# using cgr.dev/chainguard/wolfi-base as the base OS. It produces a functionally
# equivalent image without the UBI/RHEL dependency.
#
# It is intended as a copy-paste starting point. Customers are responsible for
# adapting and validating this Dockerfile for their environment.
#
# Prerequisites:
# - Binaries published to a GitHub release: https://github.com/manetu/agentvisor/releases
# - Locate SHA-256 checksums in checksums.txt attached to the same release
#
# Build example (see Quick start section for the full recommended workflow):
# export VERSION=v1.2.3
# export ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
# curl -fsSL https://github.com/manetu/agentvisor/releases/download/${VERSION}/checksums.txt \
# -o checksums.txt
# export HOST_SHA=$(awk -v a="${ARCH}" '$2 ~ "agentvisor-host-runtime-linux-"a {print $1}' checksums.txt)
#
# docker buildx build \
# --build-arg AGENTVISOR_VERSION=${VERSION} \
# --build-arg AGENTVISOR_HOST_RUNTIME_SHA256=${HOST_SHA} \
# -f Dockerfile.host \
# -t my-agentvisor-host:${VERSION} \
# .

FROM cgr.dev/chainguard/wolfi-base:latest

ARG AGENTVISOR_VERSION
ARG AGENTVISOR_HOST_RUNTIME_SHA256
ARG GVISOR_VERSION=latest

RUN test -n "${AGENTVISOR_VERSION}" || { echo "ERROR: AGENTVISOR_VERSION is required"; exit 1; }
RUN test -n "${AGENTVISOR_HOST_RUNTIME_SHA256}" || { echo "ERROR: AGENTVISOR_HOST_RUNTIME_SHA256 is required"; exit 1; }

# Install runtime dependencies.
# - shadow: provides useradd/groupadd and the SUID newuidmap/newgidmap helpers
# required for gVisor rootless user-namespace mapping.
# - ca-certificates-bundle: trust store for outbound TLS from the host-runtime.
# - curl: used only in this RUN block for binary download; removed afterward.
RUN apk add --no-cache shadow ca-certificates-bundle curl

# Create the agentvisor user (UID 1000). Corresponds to USER 1000 / ENTRYPOINT
# user in the official host image.
RUN groupadd -g 1000 agentvisor && \
useradd -u 1000 -g agentvisor -s /bin/bash --create-home agentvisor

# Enable rootless gVisor user-namespace mapping.
# runsc creates a user namespace via CLONE_NEWUSER and relies on the kernel's
# newuidmap/newgidmap SUID helpers to allow the agentvisor (UID 1000) user to
# remap to the 100000-165535 sub-UID range without CAP_SYS_ADMIN on the host.
RUN echo "agentvisor:100000:65536" >> /etc/subuid && \
echo "agentvisor:100000:65536" >> /etc/subgid

# Create directories required by the host-runtime (owned by the agentvisor user).
RUN mkdir -p /var/run/agentvisor/runtime \
/var/run/agentvisor/bundles \
/opt/guest-rootfs && \
chown -R agentvisor:agentvisor /var/run/agentvisor /opt/guest-rootfs && \
chmod 0770 /var/run/agentvisor

# Download and verify the host-runtime binary.
# Note: wolfi-base does not include /usr/local/bin — create it first.
RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \
mkdir -p /usr/local/bin && \
curl -fsSL \
"https://github.com/manetu/agentvisor/releases/download/${AGENTVISOR_VERSION}/agentvisor-host-runtime-linux-${ARCH}-${AGENTVISOR_VERSION}.tar.gz" \
-o /tmp/agentvisor-host-runtime.tar.gz && \
echo "${AGENTVISOR_HOST_RUNTIME_SHA256} /tmp/agentvisor-host-runtime.tar.gz" | sha256sum -c - && \
tar -C /usr/local/bin -xzf /tmp/agentvisor-host-runtime.tar.gz agentvisor-host-runtime && \
chmod +x /usr/local/bin/agentvisor-host-runtime && \
rm /tmp/agentvisor-host-runtime.tar.gz

# Download runsc from the official gVisor release channel.
# gVisor URLs use x86_64/aarch64, not amd64/arm64.
# See: https://gvisor.dev/docs/user_guide/install/
RUN ARCH=$(uname -m) && \
curl -fsSL \
"https://storage.googleapis.com/gvisor/releases/release/${GVISOR_VERSION}/${ARCH}/runsc" \
-o /usr/local/bin/runsc && \
chmod +x /usr/local/bin/runsc

# Harden: strip dangerous SUID password-management binaries but PRESERVE
# newuidmap and newgidmap (required for rootless gVisor).
# Also remove shadow credentials files — no runtime password auth is needed.
RUN rm -f /usr/bin/chage /usr/bin/gpasswd /usr/bin/newgrp /usr/bin/passwd && \
rm -f /etc/shadow /etc/shadow- /etc/gshadow /etc/gshadow-

# Remove the package manager and curl (defense-in-depth: reduces attack surface
# against runtime package installation on any writable tmpfs).
RUN apk del curl && \
apk del apk-tools && \
rm -rf /var/cache/apk /etc/apk /lib/apk

# ---- Environment contract (must match the "Required environment variables" table above) ----
ENV HOME=/home/agentvisor \
AGENTVISOR_SOCKET_PATH=/var/run/agentvisor/host.sock \
AGENTVISOR_GUEST_ROOTFS_PATH=/opt/guest-rootfs \
AGENTVISOR_GUEST_RUNSC_PATH=/usr/local/bin/runsc \
AGENTVISOR_GUEST_RUNTIME_ROOT=/var/run/agentvisor/runtime \
AGENTVISOR_GUEST_BUNDLE_DIR=/var/run/agentvisor/bundles \
AGENTVISOR_GUEST_SANDBOX=gvisor

LABEL io.agentvisor.type=host

USER 1000

ENTRYPOINT ["/usr/local/bin/agentvisor-host-runtime"]
Dockerfile.guest
# AgentVisor Guest Runtime — Chainguard / Wolfi reference image
#
# This Dockerfile builds a replacement for the official agentvisor-guest image
# using cgr.dev/chainguard/wolfi-base as the base OS. It produces two targets:
# - base: Minimal image with the guest-runtime binary only (no Python)
# - python: Full image with Python 3 and the AgentVisor SDK (default target)
#
# Build examples (see Quick start section for the full recommended workflow):
# export VERSION=v1.2.3
# export ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
# curl -fsSL https://github.com/manetu/agentvisor/releases/download/${VERSION}/checksums.txt \
# -o checksums.txt
# export GUEST_SHA=$(awk -v a="${ARCH}" '$2 ~ "agentvisor-guest-runtime-linux-"a {print $1}' checksums.txt)
# export SDK_WHL=$(awk '$2 ~ /^agentvisor-.*\.whl$/ {print $2}' checksums.txt)
# export SDK_SHA=$(awk -v w="${SDK_WHL}" '$2 == w {print $1}' checksums.txt)
#
# docker buildx build \
# --target python \
# --build-arg AGENTVISOR_VERSION=${VERSION} \
# --build-arg AGENTVISOR_GUEST_RUNTIME_SHA256=${GUEST_SHA} \
# --build-arg AGENTVISOR_SDK_WHL=${SDK_WHL} \
# --build-arg AGENTVISOR_SDK_SHA256=${SDK_SHA} \
# -f Dockerfile.guest \
# -t my-agentvisor-guest:${VERSION}-python \
# .

# =============================================================================
# python-deps: install Python wheels into a prefix using wolfi-base (same base
# as the final image) so Python versions match. The /tmp/pyinstall prefix is
# then COPY --from'd into the final image so no pip is needed at runtime.
# =============================================================================
FROM cgr.dev/chainguard/wolfi-base:latest AS python-deps

ARG AGENTVISOR_VERSION
ARG AGENTVISOR_SDK_WHL
ARG AGENTVISOR_SDK_SHA256

RUN test -n "${AGENTVISOR_VERSION}" || { echo "ERROR: AGENTVISOR_VERSION is required"; exit 1; }
RUN test -n "${AGENTVISOR_SDK_WHL}" || { echo "ERROR: AGENTVISOR_SDK_WHL is required"; exit 1; }
RUN test -n "${AGENTVISOR_SDK_SHA256}" || { echo "ERROR: AGENTVISOR_SDK_SHA256 is required"; exit 1; }

RUN apk add --no-cache python3 py3-pip curl

# Download and verify the SDK wheel. The proto/gRPC code is bundled inside it.
# The wheel must keep its original filename — pip validates the name format.
RUN curl -fsSL "https://github.com/manetu/agentvisor/releases/download/${AGENTVISOR_VERSION}/${AGENTVISOR_SDK_WHL}" \
-o /tmp/${AGENTVISOR_SDK_WHL} && \
echo "${AGENTVISOR_SDK_SHA256} /tmp/${AGENTVISOR_SDK_WHL}" | sha256sum -c -

# --prefix=/tmp/pyinstall lets the final stage COPY only the installed Python packages.
# /tmp is world-writable so this works under the non-root wolfi user.
RUN pip install --no-cache-dir --prefix=/tmp/pyinstall \
/tmp/${AGENTVISOR_SDK_WHL} \
requests

# =============================================================================
# base: minimal image — guest-runtime binary + required users, no Python.
# Suitable for the 'minimal' / 'interactive' image variant.
# =============================================================================
FROM cgr.dev/chainguard/wolfi-base:latest AS base

ARG AGENTVISOR_VERSION
ARG AGENTVISOR_GUEST_RUNTIME_SHA256

RUN test -n "${AGENTVISOR_VERSION}" || { echo "ERROR: AGENTVISOR_VERSION is required"; exit 1; }
RUN test -n "${AGENTVISOR_GUEST_RUNTIME_SHA256}" || { echo "ERROR: AGENTVISOR_GUEST_RUNTIME_SHA256 is required"; exit 1; }

# Install dependencies:
# - shadow: provides useradd/groupadd (removed after use)
# - nftables: guest-runtime network isolation in TCP transport mode (macOS/Windows)
# - ca-certificates-bundle: TLS trust store
# - curl: binary download (removed after use)
RUN apk add --no-cache shadow nftables ca-certificates-bundle curl

# Create the three users required by the privilege-separation model.
#
# agentvisor (UID 1000, GID 1000) — host-side identity; Docker socket auth
# guest (UID 2000, GID 1000) — guest-runtime after privilege drop;
# primary group agentvisor so it can reach the socket
# agent (UID 2001, GID 65534) — Python agent processes; uses nobody GID
# to prevent socket access
RUN groupadd -g 1000 agentvisor && \
useradd -u 1000 -g agentvisor -s /bin/bash -d /home/agentvisor --create-home agentvisor && \
useradd -u 2000 -g agentvisor -s /bin/bash -d /home/guest --create-home guest && \
useradd -u 2001 -g nobody -s /bin/bash -d /home/agent --create-home agent

# Download and verify the guest-runtime binary.
# Note: wolfi-base does not include /usr/local/bin — create it first.
RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \
mkdir -p /usr/local/bin && \
curl -fsSL \
"https://github.com/manetu/agentvisor/releases/download/${AGENTVISOR_VERSION}/agentvisor-guest-runtime-linux-${ARCH}-${AGENTVISOR_VERSION}.tar.gz" \
-o /tmp/guest-runtime.tar.gz && \
echo "${AGENTVISOR_GUEST_RUNTIME_SHA256} /tmp/guest-runtime.tar.gz" | sha256sum -c - && \
tar -C /usr/local/bin -xzf /tmp/guest-runtime.tar.gz agentvisor-guest-runtime && \
chmod +x /usr/local/bin/agentvisor-guest-runtime && \
rm /tmp/guest-runtime.tar.gz

# Create filesystem contract directories.
# /app/agent — where agent source code is mounted / baked
# /app/agent/.agentvisor — schema cache directory
# /var/run/agentvisor — socket directory (mode 0770 for agentvisor group)
RUN mkdir -p /app/agent /app/agent/.agentvisor /var/run/agentvisor && \
chown -R agentvisor:agentvisor /app /var/run/agentvisor && \
chmod 0770 /var/run/agentvisor

# Harden: remove dangerous namespace/mount/privilege-escalation tools and strip
# any remaining SUID/SGID bits. Belt-and-suspenders alongside seccomp profiles.
# Note: shadow's SUID binaries (chage etc.) are not installed by default on Wolfi,
# but the find-and-strip pass catches any apk packages that set SUID bits.
RUN rm -f /usr/bin/unshare /usr/bin/nsenter /usr/bin/mount /usr/bin/umount \
/usr/sbin/chroot /usr/bin/newgrp /usr/bin/sg 2>/dev/null || true && \
find / -xdev -type f \( -perm -4000 -o -perm -2000 \) \
-exec chmod u-s,g-s {} + 2>/dev/null || true

# Remove build-time tools (defense-in-depth against runtime package installation).
RUN apk del shadow curl && \
apk del apk-tools && \
rm -rf /var/cache/apk /etc/apk /lib/apk

# Note: AGENTVISOR_GUEST_HOST_SOCKET is injected at container launch by the host;
# do not bake it into the image (AGENTVISOR_HOST_SOCKET has no effect).
ENV PYTHONDONTWRITEBYTECODE=1

WORKDIR /app

# NOTE: No USER directive. The guest-runtime must start as UID 0 (root) so the
# privsep model works: the spawner subprocess retains UID 0 while the main
# process drops to GuestRuntimeUID (2000) via DropPrivileges().

LABEL io.agentvisor.type=guest \
io.agentvisor.image.variant=minimal \
io.agentvisor.framework.provider=interactive

ENTRYPOINT ["/usr/local/bin/agentvisor-guest-runtime"]

# =============================================================================
# python: full image — base + Python 3 + AgentVisor SDK.
# Suitable for LangGraph, CrewAI, ADK, and other Python-based agents.
#
# This stage starts fresh from wolfi-base (not FROM base) so that apk-tools is
# still available to install python3. The base stage has already removed
# apk-tools as part of its hardening, so it cannot be extended with new packages.
# All base-stage setup (users, binary, dirs, hardening) is repeated here.
# =============================================================================
FROM cgr.dev/chainguard/wolfi-base:latest AS python

ARG AGENTVISOR_VERSION
ARG AGENTVISOR_GUEST_RUNTIME_SHA256
ARG AGENTVISOR_SDK_WHL
ARG AGENTVISOR_SDK_SHA256

RUN test -n "${AGENTVISOR_VERSION}" || { echo "ERROR: AGENTVISOR_VERSION is required"; exit 1; }
RUN test -n "${AGENTVISOR_GUEST_RUNTIME_SHA256}" || { echo "ERROR: AGENTVISOR_GUEST_RUNTIME_SHA256 is required"; exit 1; }
RUN test -n "${AGENTVISOR_SDK_WHL}" || { echo "ERROR: AGENTVISOR_SDK_WHL is required"; exit 1; }

RUN apk add --no-cache shadow nftables ca-certificates-bundle curl python3

# Same user setup as base stage.
RUN groupadd -g 1000 agentvisor && \
useradd -u 1000 -g agentvisor -s /bin/bash -d /home/agentvisor --create-home agentvisor && \
useradd -u 2000 -g agentvisor -s /bin/bash -d /home/guest --create-home guest && \
useradd -u 2001 -g nobody -s /bin/bash -d /home/agent --create-home agent

# Download and verify the guest-runtime binary.
# Note: wolfi-base does not include /usr/local/bin — create it first.
RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \
mkdir -p /usr/local/bin && \
curl -fsSL \
"https://github.com/manetu/agentvisor/releases/download/${AGENTVISOR_VERSION}/agentvisor-guest-runtime-linux-${ARCH}-${AGENTVISOR_VERSION}.tar.gz" \
-o /tmp/guest-runtime.tar.gz && \
echo "${AGENTVISOR_GUEST_RUNTIME_SHA256} /tmp/guest-runtime.tar.gz" | sha256sum -c - && \
tar -C /usr/local/bin -xzf /tmp/guest-runtime.tar.gz agentvisor-guest-runtime && \
chmod +x /usr/local/bin/agentvisor-guest-runtime && \
rm /tmp/guest-runtime.tar.gz

# Copy pre-installed Python packages from the python-deps build stage.
# /tmp/pyinstall was built with --prefix=/tmp/pyinstall so it maps cleanly onto /usr.
COPY --from=python-deps /tmp/pyinstall /usr

# Verify the SDK is importable.
RUN python3 -c "import agentvisor; print('agentvisor SDK OK')"

# Pre-compile Python bytecode for faster startup on read-only rootfs.
RUN python3 -m compileall -q -j 0 /usr/lib/python3* 2>/dev/null || true

# Create filesystem contract directories (same as base stage).
RUN mkdir -p /app/agent /app/agent/.agentvisor /var/run/agentvisor && \
chown -R agentvisor:agentvisor /app /var/run/agentvisor && \
chmod 0770 /var/run/agentvisor

# Harden (same as base stage).
RUN rm -f /usr/bin/unshare /usr/bin/nsenter /usr/bin/mount /usr/bin/umount \
/usr/sbin/chroot /usr/bin/newgrp /usr/bin/sg 2>/dev/null || true && \
find / -xdev -type f \( -perm -4000 -o -perm -2000 \) \
-exec chmod u-s,g-s {} + 2>/dev/null || true

RUN apk del shadow curl && \
apk del apk-tools && \
rm -rf /var/cache/apk /etc/apk /lib/apk

# Note: AGENTVISOR_GUEST_HOST_SOCKET is injected at container launch by the host;
# do not bake it into the image (AGENTVISOR_HOST_SOCKET has no effect).
ENV PYTHONDONTWRITEBYTECODE=1

WORKDIR /app

# NOTE: No USER directive — see base stage comment.

LABEL io.agentvisor.type=guest \
io.agentvisor.image.variant=python \
io.agentvisor.framework.provider=langgraph

ENTRYPOINT ["/usr/local/bin/agentvisor-guest-runtime"]

Quick start

Save the Dockerfiles above to a local directory (e.g., mkdir agentvisor-chainguard), then:

export VERSION=v1.2.3
export ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')

# Fetch checksums for the target release
curl -fsSL \
https://github.com/manetu/agentvisor/releases/download/${VERSION}/checksums.txt \
-o checksums.txt

# Derive SHA-256 values from checksums.txt.
# The file is already release-specific, so omit VERSION from the grep patterns
# (this also avoids the PEP 440 v-prefix difference in the wheel filename).
export HOST_SHA=$(awk -v a="${ARCH}" '$2 ~ "agentvisor-host-runtime-linux-"a {print $1}' checksums.txt)
export GUEST_SHA=$(awk -v a="${ARCH}" '$2 ~ "agentvisor-guest-runtime-linux-"a {print $1}' checksums.txt)
export SDK_WHL=$(awk '$2 ~ /^agentvisor-.*\.whl$/ {print $2}' checksums.txt)
export SDK_SHA=$(awk -v w="${SDK_WHL}" '$2 == w {print $1}' checksums.txt)

# Build the host image
docker buildx build \
--build-arg AGENTVISOR_VERSION=${VERSION} \
--build-arg AGENTVISOR_HOST_RUNTIME_SHA256=${HOST_SHA} \
-f agentvisor-chainguard/Dockerfile.host \
-t my-registry/agentvisor-host:${VERSION}-chainguard \
agentvisor-chainguard/

# Build the python guest image
docker buildx build \
--target python \
--build-arg AGENTVISOR_VERSION=${VERSION} \
--build-arg AGENTVISOR_GUEST_RUNTIME_SHA256=${GUEST_SHA} \
--build-arg AGENTVISOR_SDK_WHL=${SDK_WHL} \
--build-arg AGENTVISOR_SDK_SHA256=${SDK_SHA} \
-f agentvisor-chainguard/Dockerfile.guest \
-t my-registry/agentvisor-guest:${VERSION}-chainguard-python \
agentvisor-chainguard/

Using your custom images

agentvisor build ./my-agent -t my-agent:latest \
--host-image my-registry/agentvisor-host:v1.2.3-chainguard \
--guest-image my-registry/agentvisor-guest:v1.2.3-chainguard-python

Verifying your custom image

Binary sanity check

docker run --rm my-registry/agentvisor-host:v1.2.3-chainguard \
/usr/local/bin/agentvisor-host-runtime --version

docker run --rm my-registry/agentvisor-guest:v1.2.3-chainguard-python \
/usr/local/bin/agentvisor-guest-runtime --version

Label check

docker inspect my-registry/agentvisor-guest:v1.2.3-chainguard-python \
| jq '.[0].Config.Labels'
# Expected: { "io.agentvisor.type": "guest", "io.agentvisor.image.variant": "python", ... }

End-to-end smoke test

agentvisor serve ./my-agent \
--guest-image my-registry/agentvisor-guest:v1.2.3-chainguard-python

curl http://localhost:8090/agents
# Expected: JSON list of registered agents

Compatibility & support

The reference Dockerfiles shown above are best-effort examples and are not part of the official AgentVisor test matrix. Manetu provides these as a starting point — you are responsible for validating, patching, and maintaining images derived from them.

If you discover a bug or incompatibility in the image contract documentation, please contact support@manetu.com.