Kubernetes Deployment
Deploy AgentVisor™ to Kubernetes with sandbox isolation (gVisor by default).
This page covers Kubernetes manifests and configuration. For production deployment patterns including scaling strategies, high availability, and resource planning, see the Scaling and Resilience Guide.
AgentVisor production images require a valid Manetu license key. Store it in a Kubernetes Secret and inject it via secretKeyRef. See the License Key section below and the Licensing guide for full details.
Deployment Options
AgentVisor uses gVisor by default to isolate agents using unprivileged user namespaces. gVisor provides syscall-level interception for the strongest available isolation on Linux. You can choose between two configurations:
| Approach | Description | When to Use |
|---|---|---|
| Rootless | No elevated capabilities (default) | Default; works on any cluster with unprivileged userns enabled |
| Privileged | Full privileged: true security context | When DirectFS performance is needed or userns is unavailable |
Option 1: Rootless Mode (Default)
AgentVisor uses gVisor to sandbox agent processes. gVisor's rootless mode creates a user namespace via CLONE_NEWUSER. Two host mechanisms can block this syscall and must both be relaxed:
- seccomp: Kubernetes' default
RuntimeDefaultprofile may blockCLONE_NEWUSER - AppArmor: Ubuntu 24.04+, GKE, and AKS nodes enforce AppArmor policies that restrict unprivileged user namespaces by default
Rootless mode requires unprivileged user namespaces enabled on all nodes:
- Linux 5.15+ supports this by default on most distributions
- Some distributions (Debian, Ubuntu 23.10+) require:
sysctl -w kernel.unprivileged_userns_clone=1 - Verify userns:
cat /proc/sys/kernel/unprivileged_userns_clone(should be1) orcat /proc/sys/user/max_user_namespaces(should be> 0) - Ubuntu 24.04+, GKE, AKS: also check
cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns(should be0). SetappArmorProfile: Unconfinedin the containersecurityContext(Kubernetes 1.30+) or use the annotation on pre-1.30 clusters:container.apparmor.security.beta.kubernetes.io/<container-name>: unconfined
Container images built with agentvisor build have newuidmap/newgidmap (provided by shadow-utils on UBI 9) and subuid/subgid pre-configured.
apiVersion: apps/v1
kind: Deployment
metadata:
name: agentvisor
spec:
replicas: 3
selector:
matchLabels:
app: agentvisor
template:
metadata:
labels:
app: agentvisor
spec:
containers:
- name: agent
image: my-agent:v1
securityContext:
seccompProfile:
type: Unconfined # Allows CLONE_NEWUSER needed by gVisor rootless mode
appArmorProfile:
type: Unconfined # Required on Ubuntu 24.04+, GKE, AKS
ports:
- containerPort: 8090
env:
- name: AGENTVISOR_LICENSE_KEY
valueFrom:
secretKeyRef:
name: agentvisor-license
key: license-key
- name: AGENTVISOR_TEMPORAL_TARGET
value: "temporal.temporal-system.svc:7233"
- name: AGENTVISOR_GUEST_SANDBOX
value: "gvisor"
- name: AGENTVISOR_AUTHZ_TYPE
value: "embedded"
- name: AGENTVISOR_AUTHZ_EMBEDDED_POLICY_DOMAIN_FILES
value: "/etc/agentvisor/policies/domain.yml"
volumeMounts:
- name: policies
mountPath: /etc/agentvisor/policies
readOnly: true
resources:
limits:
memory: "2Gi"
cpu: "2"
volumes:
- name: policies
configMap:
name: agentvisor-policies
Option 2: Privileged Mode
Use privileged mode when you need DirectFS performance or your nodes don't support unprivileged user namespaces.
Although the container requires privileged: true, the AgentVisor host runtime immediately drops privileges to uid/gid 1000 after initializing the gVisor sandbox. The elevated privileges are only used during the brief startup phase; all subsequent operations run as an unprivileged user.
apiVersion: apps/v1
kind: Deployment
metadata:
name: agentvisor
spec:
replicas: 3
selector:
matchLabels:
app: agentvisor
template:
metadata:
labels:
app: agentvisor
spec:
containers:
- name: agent
image: my-agent:v1
securityContext:
privileged: true # Required for gVisor privileged mode
runAsUser: 0
ports:
- containerPort: 8090
env:
- name: AGENTVISOR_LICENSE_KEY
valueFrom:
secretKeyRef:
name: agentvisor-license
key: license-key
- name: AGENTVISOR_TEMPORAL_TARGET
value: "temporal.temporal-system.svc:7233"
- name: AGENTVISOR_GUEST_SANDBOX
value: "gvisor"
- name: AGENTVISOR_GUEST_ROOTLESS
value: "false"
- name: AGENTVISOR_AUTHZ_TYPE
value: "embedded"
- name: AGENTVISOR_AUTHZ_EMBEDDED_POLICY_DOMAIN_FILES
value: "/etc/agentvisor/policies/domain.yml"
volumeMounts:
- name: policies
mountPath: /etc/agentvisor/policies
readOnly: true
resources:
limits:
memory: "2Gi"
cpu: "2"
volumes:
- name: policies
configMap:
name: agentvisor-policies
Comparison of Options
| Option | Privileges Required | Performance | Node Requirements |
|---|---|---|---|
| Rootless (default) | None | Good (9P filesystem) | Unprivileged userns enabled |
| Privileged | privileged: true | Best (DirectFS) | None |
- Use Rootless mode (default) — works on any modern cluster with unprivileged user namespaces
- Use Privileged mode when you need maximum DirectFS performance or nodes lack userns support
Service
apiVersion: v1
kind: Service
metadata:
name: agentvisor
spec:
selector:
app: agentvisor
ports:
- name: http
port: 8090
targetPort: 8090
type: ClusterIP
The http port name is referenced by the ServiceMonitor below — Prometheus's
monitoring.coreos.com operator resolves endpoints[].port against a Service's
named port, not a raw port number.
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: agentvisor
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
rules:
- host: agents.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: agentvisor
port:
number: 8090
tls:
- hosts:
- agents.example.com
secretName: agentvisor-tls
ConfigMap for Policies
apiVersion: v1
kind: ConfigMap
metadata:
name: agentvisor-policies
data:
domain.yml: |
apiVersion: iamlite.manetu.io/v1beta1
kind: PolicyDomain
metadata:
name: production
spec:
policies:
- mrn: "mrn:iam:policy:allow-authenticated"
name: allow-authenticated
rego: |
package authz
default allow = false
allow { input.principal.sub != "" }
resource-groups:
- mrn: "mrn:iam:resource-group:allowed"
name: allowed
policy: "mrn:iam:policy:allow-authenticated"
resources:
- name: openai
selector: ["mrn:agentvisor:http:api\\.openai\\.com.*"]
group: "mrn:iam:resource-group:allowed"
License Key
Store the AgentVisor license key in a Kubernetes Secret:
apiVersion: v1
kind: Secret
metadata:
name: agentvisor-license
type: Opaque
stringData:
license-key: "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
Reference it from the Deployment via secretKeyRef as shown in the examples above. See the Licensing guide for machine code configuration and offline/air-gap requirements.
Secrets
Store sensitive configuration such as API keys and credentials. For Temporal Cloud authentication secrets (API keys or mTLS certificates), see the Temporal Configuration Guide.
apiVersion: v1
kind: Secret
metadata:
name: agentvisor-secrets
type: Opaque
stringData:
api-key: "your-api-key"
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: agent
envFrom:
- secretRef:
name: agentvisor-secrets
Network Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: agentvisor-network-policy
spec:
podSelector:
matchLabels:
app: agentvisor
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- port: 8090
egress:
# Temporal
- to:
- namespaceSelector:
matchLabels:
name: temporal-system
ports:
- port: 7233
# External HTTPS (LLM APIs)
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 169.254.0.0/16
ports:
- port: 443
# DNS
- to:
- namespaceSelector: {}
ports:
- port: 53
protocol: UDP
HorizontalPodAutoscaler
Auto-scale AgentVisor pods based on resource utilization. For scaling strategy, threshold tuning, and custom metrics considerations, see the Scaling and Resilience Guide.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: agentvisor
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: agentvisor
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
PodDisruptionBudget
Protect availability during cluster maintenance and rolling updates. For high availability deployment patterns including replica counts and topology spread, see the Scaling and Resilience Guide.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: agentvisor
spec:
minAvailable: 1
selector:
matchLabels:
app: agentvisor
ServiceMonitor (Prometheus)
telemetry.metrics.enabled defaults to false, and metrics are served on the same
port as the rest of the HTTP API (there is no separate metrics port) — set
AGENTVISOR_TELEMETRY_METRICS_ENABLED=true on the Deployment's containers before
adding this ServiceMonitor, or /metrics will 404.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: agentvisor
spec:
selector:
matchLabels:
app: agentvisor
endpoints:
- port: http
path: /metrics
interval: 30s
Helm Chart (Example values)
# values.yaml
replicaCount: 3
image:
repository: my-agent
tag: v1
pullPolicy: IfNotPresent
securityContext:
seccompProfile:
type: Unconfined # Required for rootless gVisor (default mode)
appArmorProfile:
type: Unconfined # Required on Ubuntu 24.04+, GKE, AKS
# For privileged mode instead: set privileged: true, runAsUser: 0,
# and AGENTVISOR_GUEST_ROOTLESS=false
resources:
limits:
cpu: 2
memory: 2Gi
requests:
cpu: 500m
memory: 512Mi
temporal:
target: temporal.temporal-system.svc:7233
namespace: default
policies:
domain: |
apiVersion: iamlite.manetu.io/v1beta1
...
ingress:
enabled: true
host: agents.example.com
tls:
enabled: true
secretName: agentvisor-tls
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
Troubleshooting
Pod Not Starting
kubectl describe pod -l app=agentvisor
kubectl logs -l app=agentvisor
gVisor Issues
# Check container has required privileges
kubectl get pod -l app=agentvisor -o jsonpath='{.items[*].spec.containers[*].securityContext}'
# Check logs for sandbox initialization errors
kubectl logs -l app=agentvisor | grep -i "sandbox\|gvisor\|runsc"
Temporal Connection
# Test connectivity
kubectl run -it --rm debug --image=curlimages/curl -- \
curl temporal.temporal-system.svc:7233