Docker Deployment
Deploy AgentVisor™ using Docker and Docker Compose.
Building Images
Using the CLI
# Build a combined image
agentvisor build ./my-agent -t my-agent:v1
# Build with custom base images
agentvisor build ./my-agent \
-t my-agent:v1 \
--host-image ghcr.io/manetu/agentvisor/agentvisor-host:latest \
--guest-image ghcr.io/manetu/agentvisor/agentvisor-guest:latest-python
# Build and push
agentvisor build ./my-agent \
-t registry.example.com/my-agent:v1 \
--push
Multi-Architecture Builds
Build images that run on both amd64 (Intel/AMD) and arm64 (Apple Silicon, AWS Graviton):
# Build for multiple platforms and push manifest list
agentvisor build ./my-agent \
-t registry.example.com/my-agent:v1 \
--platform amd64,arm64 \
--push
The resulting image is an OCI manifest list that automatically selects the correct architecture when pulled:
# On amd64 host - pulls amd64 image
docker pull registry.example.com/my-agent:v1
# On arm64 host - pulls arm64 image
docker pull registry.example.com/my-agent:v1
# Inspect manifest list
docker manifest inspect registry.example.com/my-agent:v1
- Always use
--pushwith multi-arch builds (manifest lists require a registry) - Build and push in CI/CD rather than locally for consistent results
- Test on both architectures before deploying to production
Base Images
| Image | Description |
|---|---|
ghcr.io/manetu/agentvisor/agentvisor-host:latest | Host runtime with gVisor |
ghcr.io/manetu/agentvisor/agentvisor-guest:latest-python | Python 3.12 guest base for LangGraph agents |
ghcr.io/manetu/agentvisor/agentvisor-guest:latest-minimal | Minimal guest for interactive exec mode |
To use a different Linux distribution as the base (e.g. Chainguard/Wolfi for SBOM or FIPS requirements), see Custom Base Images.
Running Containers
Built images require a valid Manetu license key. Pass it via -e AGENTVISOR_LICENSE_KEY or use a secrets manager. See the Licensing guide for Docker Compose and Kubernetes patterns.
docker run --security-opt seccomp=unconfined \
--security-opt apparmor=unconfined \
-p 8090:8090 \
-e AGENTVISOR_LICENSE_KEY="${AGENTVISOR_LICENSE_KEY}" \
my-agent:v1
Rootless Mode (Default)
docker run --security-opt seccomp=unconfined \
--security-opt apparmor=unconfined \
-p 8090:8090 \
my-agent:v1
--security-opt seccomp=unconfined --security-opt apparmor=unconfined?AgentVisor uses gVisor to sandbox the AI agent process. gVisor's rootless mode creates a user namespace via CLONE_NEWUSER, which two host mechanisms can block:
- seccomp: Docker's default
RuntimeDefaultprofile may block the syscall - AppArmor: Ubuntu 24.04+ and many cloud Kubernetes distributions (GKE, AKS) enforce AppArmor policies that restrict unprivileged user namespaces by default
Both flags allow that syscall without granting any other elevated privileges — the container runs as uid 1000 throughout.
Privileged Mode
Opt-in when you need maximum I/O performance (DirectFS) or lack unprivileged user namespace support:
docker run --user root --privileged \
-p 8090:8090 \
-e AGENTVISOR_GUEST_ROOTLESS=false \
my-agent:v1
In privileged mode, the AgentVisor host runtime starts as root only to initialize the gVisor sandbox, then immediately drops privileges to uid/gid 1000. The elevated privileges are only used during the brief startup phase; all subsequent operations run as an unprivileged user.
Rootless mode uses gVisor's 9P filesystem instead of DirectFS, which may have some performance impact. See Sandbox Modes for details.
With Policy Files
# Rootless mode (default)
docker run --security-opt seccomp=unconfined \
--security-opt apparmor=unconfined \
-p 8090:8090 \
-v $(pwd)/policies:/etc/agentvisor/policies:ro \
-e AGENTVISOR_AUTHZ_TYPE=embedded \
-e AGENTVISOR_AUTHZ_EMBEDDED_POLICY_DOMAIN_FILES=/etc/agentvisor/policies/domain.yml \
my-agent:v1
# Privileged mode (opt-in)
docker run --user root --privileged \
-p 8090:8090 \
-v $(pwd)/policies:/etc/agentvisor/policies:ro \
-e AGENTVISOR_AUTHZ_TYPE=embedded \
-e AGENTVISOR_AUTHZ_EMBEDDED_POLICY_DOMAIN_FILES=/etc/agentvisor/policies/domain.yml \
-e AGENTVISOR_GUEST_ROOTLESS=false \
my-agent:v1
With External Temporal
docker run --security-opt seccomp=unconfined \
--security-opt apparmor=unconfined \
-p 8090:8090 \
-e AGENTVISOR_TEMPORAL_TARGET=temporal.example.com:7233 \
my-agent:v1
Docker Compose
Development Setup
# docker-compose.yml
services:
temporal:
image: temporalio/auto-setup:latest
ports:
- "7233:7233"
- "8233:8233" # UI
environment:
- DB=memory
agentvisor:
build:
context: .
dockerfile: Dockerfile
ports:
- "8090:8090"
environment:
- AGENTVISOR_TEMPORAL_TARGET=temporal:7233
- AGENTVISOR_GUEST_SANDBOX=none
volumes:
- ./my-agent:/app/agent:ro
- ./policies:/etc/agentvisor/policies:ro
depends_on:
- temporal
Images built with agentvisor build use gVisor by default. These production examples deploy with gVisor for maximum isolation. Docker sandbox mode (set AGENTVISOR_GUEST_SANDBOX=docker) is also production-grade and works on any host that supports Docker. See Sandbox Modes for the full comparison.
Production Setup — gVisor Privileged (Opt-In)
Use privileged mode when you need DirectFS performance or lack unprivileged user namespace support:
services:
temporal:
image: temporalio/auto-setup:latest
ports:
- "7233:7233"
environment:
- DB=postgresql
- POSTGRES_HOST=postgres
depends_on:
- postgres
postgres:
image: postgres:15
environment:
- POSTGRES_USER=temporal
- POSTGRES_PASSWORD=temporal
volumes:
- temporal-data:/var/lib/postgresql/data
agentvisor:
image: my-agent:v1
user: root
privileged: true
ports:
- "8090:8090"
environment:
- AGENTVISOR_LICENSE_KEY=${AGENTVISOR_LICENSE_KEY}
- AGENTVISOR_TEMPORAL_TARGET=temporal:7233
- AGENTVISOR_GUEST_ROOTLESS=false
- AGENTVISOR_AUTHZ_TYPE=embedded
- AGENTVISOR_AUTHZ_EMBEDDED_POLICY_DOMAIN_FILES=/etc/agentvisor/policies/domain.yml
volumes:
- ./policies:/etc/agentvisor/policies:ro
depends_on:
- temporal
deploy:
resources:
limits:
memory: 2G
cpus: "2"
volumes:
temporal-data:
Production Setup — gVisor Rootless (Default)
Rootless mode requires no elevated capabilities. This is the recommended default:
services:
temporal:
image: temporalio/auto-setup:latest
ports:
- "7233:7233"
environment:
- DB=postgresql
- POSTGRES_HOST=postgres
depends_on:
- postgres
postgres:
image: postgres:15
environment:
- POSTGRES_USER=temporal
- POSTGRES_PASSWORD=temporal
volumes:
- temporal-data:/var/lib/postgresql/data
agentvisor:
image: my-agent:v1
security_opt:
- seccomp=unconfined
- apparmor=unconfined
ports:
- "8090:8090"
environment:
- AGENTVISOR_LICENSE_KEY=${AGENTVISOR_LICENSE_KEY}
- AGENTVISOR_TEMPORAL_TARGET=temporal:7233
- AGENTVISOR_AUTHZ_TYPE=embedded
- AGENTVISOR_AUTHZ_EMBEDDED_POLICY_DOMAIN_FILES=/etc/agentvisor/policies/domain.yml
volumes:
- ./policies:/etc/agentvisor/policies:ro
depends_on:
- temporal
deploy:
resources:
limits:
memory: 2G
cpus: "2"
volumes:
temporal-data:
Health Checks
services:
agentvisor:
image: my-agent:v1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8090/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
Logging
services:
agentvisor:
image: my-agent:v1
environment:
- AGENTVISOR_LOG_LEVEL=info
- AGENTVISOR_LOG_FORMAT=json
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Resource Limits
services:
agentvisor:
image: my-agent:v1
environment:
- AGENTVISOR_GUEST_MEMORY_LIMIT=536870912
- AGENTVISOR_GUEST_CPU_LIMIT=1.0
deploy:
resources:
limits:
memory: 2G
cpus: "2"
reservations:
memory: 512M
cpus: "0.5"
Networking
Internal Network
services:
agentvisor:
networks:
- internal
- external
temporal:
networks:
- internal
networks:
internal:
internal: true
external:
Exposed Ports
| Port | Service |
|---|---|
| 8090 | HTTP API |
| 7233 | Temporal gRPC |
| 8233 | Temporal UI |
Secrets
services:
agentvisor:
environment:
- API_KEY_FILE=/run/secrets/api_key
secrets:
- api_key
secrets:
api_key:
file: ./secrets/api_key.txt