Open-source agent runtime — SSH-native isolation, eBPF egress policy, Kubernetes + LXC backends, GPU passthrough, MCP-native CLI
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
The open-source, self-hostable agent runtime for AI agents.
Each agent gets a persistent, SSH-reachable box with per-tenant network isolation — no kube-apiserver token, no host access, no cross-tenant leakage.
Bring your own agent — Cursor, Claude Code, OpenCode, your own MCP client.
We run the box.
AI agents are increasingly the primary user of dev infrastructure. They
want to build, install, deploy, and verify — not on the human's laptop
(too noisy, too risky, too local) but on a persistent, isolated runtime that's:檢視完整 README ↗Open-source agent runtime · SSH-native isolation · eBPF egress policy · Kubernetes + LXC · MCP-native CLI · GPU passthrough
The open-source, self-hostable agent runtime for AI agents.
Each agent gets a persistent, SSH-reachable box in an unprivileged container, with an opt-in per-tenant eBPF egress policy, audit-logged denials, and a model gateway that keeps provider API keys out of the box. What is on by default, and what each claim rests on, is in docs/security/CLAIMS.md.
Bring your own agent — Cursor, Claude Code, OpenCode, your own MCP client. We run the box.
agent: "create me a sandbox called 'blog'" → containarium create
agent: "wire up SSH so I can reach it" → containarium ssh-config sync
agent: "install Caddy on :8080 inside the box" → shell_exec (via agent-box MCP)
agent: "expose that on blog.example.com" → containarium expose-port
curl https://blog.example.com → hello world
🌐 Project site: containarium.dev · 🎬 55s demo: youtu.be/IBDDD_tb8FY · 🚀 Live app: helloworld.demo.containarium.dev
| Path | You end up with | Go to |
|---|---|---|
| Hosted cloud | a box you can ssh into with your agent wired to it, no server to run | Sign up at cloud.containarium.dev |
| Self-host on a VM | the same, on your own Ubuntu VM (about 5 minutes) | Quick start |
| See the end result | a live app served from a box, which is what you end up with | helloworld.demo.containarium.dev |
AI agents are increasingly the primary user of dev infrastructure. They want to build, install, deploy, and verify — not on the human's laptop (too noisy, too risky, too local) but on a persistent runtime that's:
docs/security/CLAIMS.md.)systemd, real networking,
and the ability to host things on the open internet.That's the runtime Containarium gives you. It runs as a self-hosted
platform on LXC or Kubernetes, exposes its admin surface over MCP, and
ships a second MCP server that lives inside the box so the agent can
shell_exec and edit files directly.
You bring the agent. We run the box.
One path, three steps, with a check at the end of each. You're done when a
box exists, ssh alice gets you a shell in it, and your agent can run
commands inside it.
Before you start: a fresh Ubuntu 24.04 VM you can sudo on (see
System requirements). You need Containarium
v0.96.0 or later; the installer fetches the latest release. sudo is
only needed to run the installer and to read the admin token. Every other
command runs as your normal user and goes through the daemon's API.
On the VM:
curl -fsSL https://containarium.dev/install.sh | sudo bash
✅ Check: the daemon is running and the CLI is on your PATH.
systemctl is-active containarium # → active
containarium version # → v0.96.0 or later
Point the CLI at the local daemon, using the admin token the installer
saved, and create a box called alice:
export CONTAINARIUM_HTTP=true
export CONTAINARIUM_SERVER=http://localhost:8080
export CONTAINARIUM_TOKEN="$(sudo cat /etc/containarium/admin.token)"
containarium quickstart alice
quickstart creates the box, reuses your ~/.ssh key or generates a
managed one (~/.ssh/containarium_ed25519), writes
~/.containarium/ssh_config, adds the single
Include ~/.containarium/ssh_config line to ~/.ssh/config, and wires
the box into your agent's MCP config (Claude Code by default). It's safe
to run again, including to repair an older quickstart's MCP command.
✅ Check: quickstart ends with ✓ quickstart complete, and the box is
listed as running.
containarium list # → a row for alice-container, STATUS running
Choose the variant that matches where you're typing.
Variant A: on the VM. This works on a fresh install. Step 1 already wrote the config. Re-sync whenever your boxes change:
containarium ssh-config sync --identity ~/.ssh/containarium_ed25519
# leave out --identity if quickstart reused your own ~/.ssh key
✅ Check:
ssh alice hostname # → alice-container
Variant B: from your laptop. On a single VM, ssh-config sync adds a
ProxyJump through the VM's per-user jump account when the daemon doesn't
advertise ssh_host. If the API is reached directly, the jump host defaults
to the API hostname, on SSH port 22. With the API tunnel below, name the
actual VM with --jump-host (use <vm-host>:<ssh-port> for a custom SSH port).
Deployments with a sentinel can
use --sentinel <sentinel-host> instead; a daemon's --ssh-host still takes
precedence over the jump route.
# On your laptop: install the client only.
curl -fsSL https://raw.githubusercontent.com/footprintai/containarium/main/hacks/install-cli.sh | sudo bash
# Reach the daemon's API through an SSH tunnel to the VM.
ssh -fN -L 8080:localhost:8080 <you>@<vm-host>
export CONTAINARIUM_HTTP=true
export CONTAINARIUM_SERVER=http://localhost:8080
export CONTAINARIUM_TOKEN="$(ssh <you>@<vm-host> sudo cat /etc/containarium/admin.token)"
containarium ssh-config sync --jump-host <vm-host>
Then make Include ~/.containarium/ssh_config the first line of your
laptop's ~/.ssh/config. Use the key whose public half was supplied when
creating the box: that key is authorized on both the box and its VM jump
account. Add --identity ~/.ssh/<your-private-key> to sync if needed; it
applies to both SSH connections.
✅ Check:
ssh alice-container hostname # → alice-container
Your agent reaches the box through agent-box, an MCP server that runs
inside it. code install puts it there, along with Claude Code. Run it on
the VM, in the same shell as step 1:
containarium code install alice
✅ Check: the install prints agent-box installed at …/.local/bin/agent-box,
and the binary is there:
ssh alice 'test -x ~/.local/bin/agent-box && echo agent-box ready' # → agent-box ready
On the machine where your agent runs, quickstart wires the MCP config
automatically. Step 1 already did this for Claude Code on the VM. If your
agent runs on your laptop, run this there using the API connection from
step 2; it reuses the existing box:
containarium quickstart alice --agent claude
# For Gemini or Codex, use --agent gemini or --agent codex instead.
For Cursor, add the server to ~/.cursor/mcp.json manually:
{
"mcpServers": {
"containarium-box": {
"command": "ssh",
"args": ["alice", "sh -c 'PATH=\"$HOME/.local/bin:/usr/local/bin:$PATH\" exec agent-box'"]
}
}
}
This command sets the box's PATH for non-interactive SSH and supports
both user-level and system-wide installs of agent-box.
✅ Check: your agent lists containarium-box as a connected MCP server
(in Claude Code, claude mcp list). Asking it to "run uname -a in the
box" returns a Linux kernel line from alice-container. It now has
shell_exec, read_file, write_file, list_directory, move_file and
delete_file inside the box.
That's your first box. Next: run the agent on the box · run pi inside the box · put the box on a public hostname · build a site in one command.
code install (step 3) already put Claude Code on the box. It installs
no credential: sign-in goes through Anthropic's own flow, never through
us. Choose one of these, once:
# interactive: sign in inside the box (device code)
ssh alice
claude
# or headless: your own key in the "env" block of ~/.claude/settings.json on the box
# {"env": {"ANTHROPIC_API_KEY": "<your key>"}}
Then:
containarium code run alice --prompt "add a health endpoint and run the tests"
code run streams output to your terminal as it's produced, but it isn't a
pipe. The run is detached on the box and its output is captured to a log;
your terminal is a resumable reader over that log. Close your laptop, lose
wifi, press Ctrl-C: the run keeps going, and containarium code attach alice
picks the stream back up byte-exact, with nothing missing and nothing
repeated.
containarium code attach alice # reconnect; replays what you missed
containarium code status alice # liveness, and the exit code once it finishes
containarium code stop alice # reap it; the log stays readable
The difference from the MCP wiring in step 3 is where the agent runs. There, the agent is on your machine and reaches into the box. Here, the agent is on the box, so the work survives your machine sleeping, and the tests run next to the code instead of over a network hop.
If your agent has no MCP client, run it inside the box instead. See docs/integrations/pi.md for the pi walkthrough (installs, keys, code sync, sessions).
containarium expose-port alice \
--container-port 8080 \
--domain blog.example.com
Caddy on the sentinel terminates TLS for blog.example.com and forwards to
alice-container:8080. curl https://blog.example.com then hits whatever
alice is serving on port 8080.
Run from your laptop against a server you can reach (see step 2, variant B),
quickstart does steps 1–3 in one go: it creates the box and wires SSH and
your agent (claude, gemini or codex). With --prompt it also exposes
--domain and launches your local agent on the build:
containarium quickstart alice --server <your-server> \
--prompt "a coffee-shop landing page" --domain coffee.example.com
containarium quickstart --help lists every option.
Every action in Containarium has a CLI verb (canonical) AND an MCP tool (thin wrapper that delegates to the same Go function). See CLAUDE.md for the convention.
agent-box — in-the-box MCP serverRuns inside every container. Reached over stdio (typically wrapped by SSH on the client side). Exposes Linux-native operations:
| Tool | What it does |
|---|---|
shell_exec | Run a shell command, capture stdout/stderr/exit, bounded by timeout (default 30s, max 10min) and 256 KiB output cap |
read_file | Byte range OR head=N lines OR tail=N lines |
write_file | Atomic write with mkdirp (temp + rename) |
list_directory | Type/size/mtime, hidden filtering |
move_file | Atomic rename with mkdirp on destination |
delete_file | Single-file remove (refuses directories so recursive deletes go via shell_exec where blast radius is explicit) |
Resources (read-only data the agent fetches via MCP resources/read):
| URI | What it returns |
|---|---|
containarium://ci-context | JSON metadata about the current CI run (PR number, commit SHA, failing test, etc.) when the box was kept alive by the FootprintAI/containarium-run GitHub Action after a failed CI run. Returns {"available": false} on non-CI boxes so callers never have to special-case errors. |
containarium://ci-prompt | Static markdown playbook telling agents how to debug a failing CI run inside this box (what to read first, how to iterate, what not to do). Same body on every box; pair with ci-context for the per-run data. |
Optional sandbox: when AGENTBOX_ROOT is set, every file-ops path is
resolved against that root with a boundary-aware prefix check. Default
unset = no constraint. See
internal/agentbox/ for the Go implementation.
mcp-server — platform MCP serverRuns on the host. Exposes the platform's outside-the-box operations as
MCP tools — about a hundred of them, one per CLI verb: container lifecycle
(create_container, list_containers, start_container,
stop_container, delete_container, resize_container), networking
(expose_port, sync_ssh_config, passthrough routes), coding-agent runs
(code_run, code_attach, code_status, code_stop), secrets, backups,
recipes, security scans, tracker connections, and get_metrics /
get_system_info. A token's scopes decide which ones a client sees. See
cmd/mcp-server/ and
internal/mcp/ for the catalog.
containarium CLISame surface as the platform MCP, plus deeper administration. The verbs
this README uses (the CLI has about ninety — containarium --help lists
them all):
containarium quickstart Create a box, wire SSH + your agent, optionally build a site
containarium create Create a new container
containarium list List all containers
containarium wake / sleep Start a stopped box / stop a running one
containarium delete Delete a container
containarium code Run a coding agent ON a box (install / run / attach / logs / runs / status / stop / egress-policy)
containarium expose-port Expose container:port on a public hostname
containarium ssh-config Generate self-contained ssh_config (show / sync)
containarium route Manage proxy routes (low-level)
containarium passthrough-route Manage TCP/UDP passthrough routes (daemon API)
containarium token Issue, inspect, refresh and revoke JWT tokens
containarium login / connect Device-flow sign-in to a server; open a shell on a box with that token
containarium info Container or system info
containarium version Print version
Host-side verbs (daemon, sentinel, passthrough, audit, doctor,
…) live in containariumd — see Two binaries.
Run containarium <verb> --help for full options.
The sentinel is a tiny always-on VM (e2-micro on GCP free tier works) that:
See docs/SENTINEL-DESIGN.md for the full design.
Agent (Cursor / Claude Code / OpenCode)
│
│ JWT (access; tt=access, jti, scopes)
│ MCP over stdio ──┐
│ │ ┌── refresh ──> POST /v1/tokens/refresh
v ▼ │ (single-use; old jti revoked)
ssh user@box → sshpiper → agent-box (in container)
│
│ HTTPS (mTLS upstream; PROXY-protocol v2)
v
Sentinel (e2-micro, always-on)
├── sshpiper (port 22) : routes by username; fail2ban per-user
├── Caddy + PROXY-protocol (443) : routes by hostname / SNI suffix
└── /wake/ source-IP allowlist : trusted-proxy only
│
v
+-------------------------------------------------+
| Backend VM (spot or bare-metal GPU node) |
| |
| Incus (LXC) ── containers |
| ├── alice-container : SSH + agent-box |
| │ └── /run/secrets/* : tmpfs, 0440 alice |
| └── bob-container : ZFS-backed storage |
| |
| Containarium daemon |
| ├── JWT auth (iss/aud/jti/scopes) |
| ├── Admin RBAC + container-owner authz |
| ├── Image-digest gate (REQUIRE + VERIFY) |
| ├── Secrets ── Postgres (envelope-encrypted) |
| │ │ |
| │ v |
| │ KMS ── Vault Transit / GCP KMS |
| │ (master key retirable post-cutover)
| └── Audit log ── Postgres + SHA-256 hash |
| chain (verify CLI) |
+-------------------------------------------------+
A single sentinel can front multiple backend VMs — a "pool" — and a single deployment can run multiple pools (each with its own backends). See docs/MULTI-POOL.md.
Security control surface (all opt-in via env, default-off for
upgrade safety; see docs/security/OPERATOR-SECURITY-RUNBOOK.md):
| Env var | Layer | Effect |
|---|---|---|
CONTAINARIUM_REQUIRE_IMAGE_DIGEST=true | API | refuse images without @sha256:<64hex> |
CONTAINARIUM_VERIFY_IMAGE_DIGEST=true | API | verify digest against the registry index (pre- + post-pull) |
CONTAINARIUM_ALLOWED_IMAGE_REGISTRIES | API | restrict which simplestreams remotes the daemon will pull from |
CONTAINARIUM_KMS_BACKEND={none,inproc,vault,gcp} | Secrets | envelope-encrypt DEKs through an external KMS |
CONTAINARIUM_REQUIRE_ENVELOPE=true | Secrets | refuse legacy master-key-only rows (Phase E retirement gate) |
CONTAINARIUM_POSTGRES_URL_FILE / _PASSWORD_FILE | Secrets | DB creds from disk rather than env |
CONTAINARIUM_WAKE_TRUSTED_PROXIES | Sentinel | source-IP allowlist for /wake/ |
OTEL_BEARER_REQUIRED=true | Telemetry (collector-side; set on the OTel collector, not the daemon) | collector rejects un-bearered OTLP submissions |
These give you sandboxes for AI agents, but only as hosted SaaS:
systemd, real network
namespaces, GPU passthrough. Not a process-per-call sandbox.sbx)Docker's sbx run claude and Containarium both call themselves
"AI sandboxes," but they sit on opposite ends of the same spectrum:
sbx runs the sandbox on the developer's laptop
(microVM, host-isolation). Containarium runs the sandbox on a VM
you host (LXC, multi-tenant, public-internet reachable via the
sentinel).
sbx is session-shaped (workspace mount, no
documented "give me a box that survives reboot and has a
hostname"). Containarium containers persist indefinitely, with
ZFS snapshots and 30-day retention.containarium expose-port alice --domain blog.example.com is one verb. sbx is laptop-local; no
public-hostname story.sbx is CLI-first (sbx run <agent>).
Containarium is MCP-native — two MCP servers (in-the-box
agent-box + platform mcp-server) plus the same surface via
CLI, SSH, REST/gRPC, and a web UI.sbx CLI is free; team policy (Docker Admin Console)
is a paid subscription. Containarium is Apache 2.0 — including
the audit log, RBAC, KMS integrations, and everything else on
this page.If you're stopping an agent from rm -rf-ing the laptop it's
running on, sbx is the lighter tool. If you're giving your agent
(or your customer's agent) a persistent Linux box on the public
internet, Containarium is the shape.
kubernetes-sigs/agent-sandbox
and NVIDIA/OpenShell are the
closest open-source peers on Kubernetes:
kubectl exec or a proprietary client, which requires
the agent to hold a kube-apiserver token or cluster credentials.
Containarium reaches the pod over SSH through sshpiper — the agent has
no path to the cluster control plane at all.agent-box MCP server runs inside the box,
reachable over SSH stdio — any MCP-speaking agent (Claude Code, Cursor,
OpenCode) works with zero client library.containarium CLI and --runtime flag. You
switch backends without changing anything for the agent.docs/security/CLAIMS.md).
agent-sandbox has NetworkPolicy; OpenShell has eBPF but is
NVIDIA-stack-specific. Neither offers a portable, per-tenant eBPF
allowlist across LXC and K8s backends.Those are persistent IDEs. Containarium is a persistent box — agent-driven, not developer-driven, no IDE assumption, SSH-as-the-API:
LXC is a system container, not an application container. Each
container has systemd, a real init, real users, real package managers,
real sudo. You can run Docker inside a Containarium container; the
reverse isn't really a thing.
If your agent is going to apt install half a Linux distro, edit
config files in /etc, run a database, and reboot — LXC is the right
shape. If your agent runs a single Python process, Docker or Modal is
fine.
It isn't either/or: Containarium can run a box as a pod in a
Kubernetes cluster you already operate — same SSH-native agent contract,
no kube-apiserver token in the agent's hands. Switch with
--runtime=k8s. See the Kubernetes backend section below.
Beyond the agent-native primitives, Containarium ships:
For ML/AI agent workflows. Works with NVIDIA RTX 3090, RTX 4090, and similar. PCI-level passthrough so the container sees the GPU directly. Tested on bare-metal GPU nodes connected to the sentinel via tunnel.
A single sentinel can front:
All containers from all backends appear in a single unified API.
Beyond the LXC/Incus backend, Containarium can run a box as a pod in a Kubernetes cluster you already operate — reached over SSH exactly like an LXC box, so an agent can't tell which substrate it landed on. The daemon reconciles a per-tenant namespace + StatefulSet + headless Service
Pipe CRD) so ssh <tenant>@<gateway> routes to the right pod.
What the NetworkPolicy does and does not guarantee is in
docs/security/CLAIMS.md.The pitch isn't "another way to run pods" — it's giving an agent a
hardened, SSH-native foothold in your cluster without handing it a
kube-apiserver token: the box runs with automountServiceAccountToken: false and satisfies the restricted Pod Security profile. Compare this
to kubectl exec-based runtimes where the agent necessarily holds cluster
credentials.
Runtime selection (no recompile needed):
# LXC/Incus (default)
containariumd daemon
# Kubernetes — uses in-cluster config or KUBECONFIG
CONTAINARIUM_RUNTIME=k8s containariumd daemon
# or
containariumd daemon --runtime=k8s
Both backends share the same CLI, MCP tools, JWT auth, and REST/gRPC API.
GPU passthrough is supported on K8s via nvidia.com/gpu resource limits.
Local bring-up takes under 5 minutes with kind — see
docs/KIND-QUICKSTART.md.
Design, topology, and BYO-cluster integration: docs/K8S-AGENT-BOX-RUNTIME-DESIGN.md.
A basic dashboard at /webui/ for users who'd rather not type CLI:
container list, lifecycle controls, metrics, browser-based terminal.
Polished UI is intentionally a cloud-product concern — the OSS web UI
is functional, not opinionated.
Containers survive VM restarts and spot termination. ZFS handles compression, snapshots (daily by default, 30-day retention), and checksums.
The sentinel itself is e2-micro (free tier). It:
VictoriaMetrics + Grafana auto-provisioned. Per-container CPU, memory, disk, network. Alerting via webhooks. SSH audit logs per user (LXC backend; the Kubernetes backend records control-plane API audit but not in-box SSH sessions — see #1189).
/usr/sbin/nologin on the sentinel,
users can only proxy through to their container.Zero-trust controls (see docs/security/OPERATOR-SECURITY-RUNBOOK.md; several are opt-in, see docs/security/CLAIMS.md for what is on by default):
iss / aud / jti / tt / scopes: 32-byte minimum
secret enforced at startup; refresh tokens are single-use; jti-based
revocation; per-tool MCP scopes propagate to server-side gates.--delivery=file for secrets that shouldn't be visible in
/proc/<pid>/environ.containariumd audit verify
(on the host): tamper-evident against an application- or database-level
attacker. An attacker who can write the database can rewrite the chain
consistently; the chain root can be anchored to a file outside the database
(internal/audit/anchor.go), which a database credential does not reach, but
a host-root attacker could still edit both.volatile.base_image defense-in-depth
for supply-chain hardening.SECURITY.md with a 90-day coordinated-disclosure window;
gosec / govulncheck / trivy running in CI.# Create (Ubuntu 24.04, default)
containarium create alice --ssh-key ~/.ssh/id_ed25519.pub
# Create with options
containarium create ml-dev \
--ssh-key ~/.ssh/id_ed25519.pub \
--gpu 0 \
--stack gpu \
--memory 16GB \
--cpu 4
# Lifecycle
containarium list
containarium info
containarium wake alice # start a stopped box
containarium sleep alice # stop it
containarium delete alice
# Expose a container port on a public hostname
containarium expose-port alice \
--container-port 8080 \
--domain blog.example.com
# Lower-level route management
containarium route add api.example.com --target 10.0.3.42:3000
containarium route list
containarium route delete api.example.com
# Raw TCP/UDP passthrough via local iptables (on the host, so it's a
# containariumd verb)
containariumd passthrough add --port 50051 \
--target-ip 10.0.3.150 --target-port 50051
# Raw TCP/UDP passthrough via the daemon API (works against a remote box)
containarium passthrough-route add --port 9443 \
--target-ip 10.0.3.150 --target-port 50051 --server <host:port>
containarium passthrough-route list --server <host:port>
containarium passthrough-route remove --port 9443 --server <host:port>
# Print to stdout (preview)
containarium ssh-config show
# Write to ~/.containarium/ssh_config (one-line `Include` to wire in)
containarium ssh-config sync
containarium ssh-config sync --sentinel sentinel.example.com # via sentinel
containarium ssh-config sync --identity ~/.ssh/containarium_ed25519
# From a client machine, against a remote server (the bare form above
# only reaches a local Incus socket)
containarium ssh-config sync --http --server <host:port>
# Issue an access + refresh pair (CLI-only; never exposed via API).
# Access tokens are short-lived (default 15 min) and authenticate the
# API. Refresh tokens are long-lived and single-use — exchange via
# POST /v1/tokens/refresh for a new pair.
containarium token generate \
--username admin \
--roles admin \
--secret-file /etc/containarium/jwt.secret
# Use the access token
curl -H "Authorization: Bearer <access-token>" http://localhost:8080/v1/containers
# Exchange the refresh token for a new pair (same as POST /v1/tokens/refresh;
# the refresh token is the credential, so no --token)
containarium token refresh --refresh-token "$REFRESH" --server <host:port>
# Inspect a token's claims (jti, scopes, expiry, validation)
containarium token inspect <token> --secret-file /etc/containarium/jwt.secret
# Revoke a leaked token by jti (idempotent; reads from `audit query`
# or `token inspect`)
containarium token revoke <jti> --reason "leak_2026_05_22"
containarium token list-revoked
# Mint a least-privilege token for an agent with only the scopes it
# needs — server-side gates enforce this even if the agent ignores
# the filter.
containarium token generate \
--username alice-agent \
--scopes containers:read,containers:write \
--secret-file /etc/containarium/jwt.secret
See docs/security/OPERATOR-SECURITY-RUNBOOK.md
for the full token lifecycle, leak-response playbook, and the agent
least-privilege scope catalog.
containarium vs containariumdSame convention as docker/dockerd or incus/incusd: containarium
is the CLI you run from your laptop (create, list, ssh-config,
expose-port, …); containariumd is what runs on the host — the
daemon, the sentinel, and every on-host admin verb (daemon,
sentinel, service install, sync-accounts, pool join, node,
hosting, recover, doctor). Install containarium wherever you
want to drive the platform from (your laptop, a jump box); install
containariumd on every host that runs the daemon or sentinel itself.
A compat symlink (/usr/local/bin/containarium -> containariumd) keeps
existing hosts and scripts that still invoke containarium <daemon verb>
working during the rollout.
curl -fsSL https://containarium.dev/install.sh | sudo bash
See hacks/README.md for what the script does.
cd terraform/gce
cp examples/single-server-spot.tfvars terraform.tfvars
vim terraform.tfvars # set project_id, admin_ssh_keys, allowed_ssh_sources
terraform init
terraform apply
See terraform/gce/README.md for variables.
overlay, br_netfilter, nf_nat (Docker in
containers needs these).# Quick Incus install via Zabbly
curl -fsSL https://pkgs.zabbly.com/key.asc | \
sudo gpg --dearmor -o /usr/share/keyrings/zabbly-incus.gpg
echo 'deb [signed-by=/usr/share/keyrings/zabbly-incus.gpg] \
https://pkgs.zabbly.com/incus/stable noble main' | \
sudo tee /etc/apt/sources.list.d/zabbly-incus-stable.list
sudo apt update
sudo apt install incus incus-tools incus-client
incus --version # 6.19 or later
Containarium exposes:
http://localhost:8080 (gRPC-gateway over the gRPC
service, JWT auth):50051 (mTLS, primarily used by the CLI)mcp-server (platform) and agent-box
(in-the-box)OpenAPI / Swagger UI at
http://localhost:8080/swagger-ui/.
Token-issuance is CLI-only by design; the daemon does not have an "issue token via API" endpoint, because if it did, anyone with API access could mint admin tokens.
nologin and only routes
through.authorized_keys content in the container.If you're running an untrusted agent, set AGENTBOX_ROOT to a project
directory:
# In the container
export AGENTBOX_ROOT=/srv/project
agent-box # all file ops now constrained to /srv/project
shell_exec is intentionally not constrained beyond the LXC container
boundary itself — by design, that's the tool's contract. If you need
tighter isolation, run agent-box in a more restrictive container (e.g.
nested LXC, or chroot the user account further).
allowed_ssh_sources in Terraform
(or firewall rules manually) to lock down who can hit port 22.Why not Docker / Podman?
Docker is for application containers. Containarium uses LXC system
containers — full Linux OS per container, real systemd, native SSH,
Docker-in-LXC works, persistent filesystem. If your agent will
apt install and reboot, you want LXC.
Why not Kubernetes?
K8s orchestrates application containers across nodes — that's the
infrastructure layer. Containarium is the agent runtime that runs on
top of Kubernetes (or LXC), giving each agent a persistent, SSH-native
box without handing it cluster credentials. If you're already on K8s,
run containariumd daemon --runtime=k8s and use your existing cluster as
the backend. See the Kubernetes backend
section and docs/KIND-QUICKSTART.md.
Why not Vagrant? Vagrant orchestrates VMs on a developer's local machine. Containarium hosts environments on shared remote infrastructure for many agents.
Why not Dev Containers / VS Code Remote Containers? Dev Containers are project-scoped, IDE-coupled, single-developer. Containarium gives many users (or many of one user's agents) their own persistent boxes on shared infrastructure, IDE-agnostic.
Why not Codespaces / Gitpod? Browser-IDE-as-a-Service, per-hour billed, vendor-locked. Containarium is self-hosted, persistent, SSH/MCP-based, no per-hour billing in OSS.
Why not e2b / Modal / Daytona? Closest peers — sandboxes for AI agents. They're SaaS-only and typically optimize for short-lived, process-per-call execution. Containarium is self-hostable, MCP-native, and gives you full persistent Linux boxes. Pick e2b if you want hosted-only and ephemeral; pick Containarium if you want self-hosted, persistent, and your data on your infra.
Why LXC at all?
systemd.sudo.docs/security/CLAIMS.md).Where Containarium has been demonstrated live:
Demoed Containarium somewhere? Open a PR and add it here.
main and recent
releases.nvidia.com/gpu passthrough. Selected at runtime via --runtime=k8s (no
recompile). End-to-end validated on kind; 5-minute local bring-up in
docs/KIND-QUICKSTART.md. See also
docs/K8S-AGENT-BOX-RUNTIME-DESIGN.md.agent-box MCP, ssh-config CLI, expose-port CLI, and
the demo recording — the Quick start above is built on
them.agent-box tier-2 (MCP Roots, background process
management), demo-driven docs and examples.If you want to drive an item, open an issue or PR — community work is welcome and we triage weekly.
containarium <verb> first; MCP wraps it).No CLA. Apache 2.0 means you can use, modify, and redistribute. We welcome PRs that align with the project's positioning and reject those that don't (e.g. "let me add multi-tenancy to the OSS daemon" goes into the cloud repo discussion, not here).
Apache License 2.0 — see LICENSE.