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>.
| Artifact | Description |
|---|---|
agentvisor-host-runtime-linux-amd64-VERSION.tar.gz | Licensed+obfuscated host-runtime (x86-64) |
agentvisor-host-runtime-linux-arm64-VERSION.tar.gz | Licensed+obfuscated host-runtime (ARM 64) |
agentvisor-guest-runtime-linux-amd64-VERSION.tar.gz | Guest-runtime binary (x86-64) |
agentvisor-guest-runtime-linux-arm64-VERSION.tar.gz | Guest-runtime binary (ARM 64) |
agentvisor-<PYVER>-py3-none-any.whl | Python SDK wheel (proto/gRPC code bundled) |
checksums.txt | SHA-256 sums for all of the above |
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.
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
| Path | Description |
|---|---|
/usr/local/bin/agentvisor-host-runtime | AgentVisor host binary (ENTRYPOINT). Path is fixed; not configurable. |
/usr/local/bin/runsc | gVisor runsc binary |
/usr/bin/newuidmap | SUID helper for rootless user-namespace mapping |
/usr/bin/newgidmap | SUID helper for rootless user-namespace mapping |
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
| Entry | UID/GID | Purpose |
|---|---|---|
agentvisor group | GID 1000 | Shared group for socket directory access |
agentvisor user | UID 1000 | Runtime user; image runs as USER 1000 |
/etc/subuid entry | agentvisor:100000:65536 | Sub-UID range for rootless namespace mapping |
/etc/subgid entry | agentvisor:100000:65536 | Sub-GID range for rootless namespace mapping |
Directories
| Path | Owner | Mode | Purpose |
|---|---|---|---|
/var/run/agentvisor | agentvisor:agentvisor | 0770 | Runtime 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):
| Variable | Required 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_SANDBOX | gvisor |
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
| Path | Description |
|---|---|
/usr/local/bin/agentvisor-guest-runtime | AgentVisor 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
| User | UID | Primary GID | Purpose |
|---|---|---|---|
agentvisor | 1000 | 1000 | Socket directory ownership; host-side identity in Docker transport |
guest | 2000 | 1000 (agentvisor) | Guest-runtime process after privilege drop; shares socket GID |
agent | 2001 | 65534 (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
| Path | Owner | Mode | Purpose |
|---|---|---|---|
/app/agent | agentvisor:agentvisor | 0770 | Agent source code (fixed path — required by the sandbox) |
/app/agent/.agentvisor | agentvisor:agentvisor | 0770 | Schema cache |
/var/run/agentvisor | agentvisor:agentvisor | 0770 | Socket directory |
/app/agent is a fixed contract pathThe 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
| Variable | Value | Notes |
|---|---|---|
PYTHONDONTWRITEBYTECODE | 1 | Recommended in image |
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:
| Requirement | Notes |
|---|---|
Python ≥ 3.10 reachable as python3 | Symlink at /usr/bin/python3 or /usr/local/bin/python3 |
agentvisor wheel installed system-wide | From agentvisor-VERSION-py3-none-any.whl (proto/gRPC code is bundled — no separate proto wheel needed) |
requests installed | Standard 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.
| Label | Example | Description |
|---|---|---|
io.agentvisor.type | guest | Advisory; logged at startup for observability |
io.agentvisor.image.variant | python or minimal | Informational; logged at startup |
io.agentvisor.framework.provider | langgraph, crewai, adk, strands, interactive | Sets the agent framework interpreter |
io.agentvisor.exec-mode | false | Set 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 /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.