Configuration keys

User config keys and how the layers resolve

AgentBox reads layered defaults at the start of every command. The same key shape lives in three files plus a runtime CLI-flag layer, so a preference can be a one-off flag, sticky for one project, or shared with your whole team.

You rarely need to touch config — every key has a built-in default and most surfaces also expose a one-off CLI flag. Config is for making a preference stick.

TIP

Don't memorize keys. agentbox config list prints every key with its current value and where it came from, and agentbox config list --include-advanced adds the advanced ones.

Config layers

AgentBox merges four layers on top of the built-in defaults. Highest layer that sets a key wins:

CLI flag > workspace defaults: > per-project > global > built-in default
LayerWhere it livesScope
CLI flagthe command you typethis invocation only, never persists
workspacethe defaults: block in agentbox.yamlcommitted, shared with the team
per-project~/.agentbox/projects/<hash>/config.yamlper machine, per user, not committed
global~/.agentbox/config.yamlevery project on this machine
built-inBUILT_IN_DEFAULTS in the CLIthe fallback for any unset key

The per-project <hash> is the first 16 hex chars of the SHA-1 of the project's absolute path. The workspace layer is found by walking up from the current directory to the nearest agentbox.yaml.

config set/unset/edit only write the --global or --project files (project is the default). The committed defaults: block is hand-edited — see agentbox.yaml for its full schema. engine.kind is the one key applied at CLI startup; every other key flows through the per-command effective-config load.

Config resolution order: the highest layer that sets a key wins; unset keys fall through to the built-in default.

The config command

Seven subcommands: get, set, unset, list, path, edit, list-projects. The default write scope is --project. Pass at most one scope flag.

# Read the effective value of a key (and where it came from)
agentbox config get box.provider

# Set a key (default scope is --project)
agentbox config set box.withPlaywright true

# Set a key globally for all projects
agentbox config set box.provider hetzner --global

# Remove a key from a scope (default --project)
agentbox config unset box.image --project

# List effective config; advanced keys hidden unless you ask
agentbox config list
agentbox config list --include-advanced

# List one layer's raw values
agentbox config list --scope global
agentbox config list --scope workspace

# Print or open a scope's file
agentbox config path --workspace
agentbox config edit --global

# Show directories that have per-project config recorded
agentbox config list-projects

config get --all shows every layer's value with its source — the fastest way to see why a key resolved the way it did:

$ agentbox config get box.provider --all
box.provider:
  effective: hetzner   (global ~/.agentbox/config.yaml)
  cli:       <unset>
  workspace: <unset>
  project:   <unset>   ~/.agentbox/projects/3f2a…/config.yaml
  global:    hetzner   ~/.agentbox/config.yaml
  default:   docker

<unset> is the literal token the CLI prints for an absent value. get, set, list, path, and list-projects all accept --json. set coerces the value to the key's declared type (bool/int/enum/string) and errors on an invalid enum or non-int. There is no config set --workspace; to change the committed defaults:, edit agentbox.yaml (or run config edit --workspace, which just opens that file).

WHY

agentbox config get <key> validates the key — an unknown key errors. Use it to sanity-check spelling before set.

HEADS UP

box.snapshot was renamed to box.hostSnapshot. The old name now errors with a migration hint — update any config or agentbox.yaml still using it. AgentBox is pre-1.0, so there are no compatibility aliases.

box.* — the box keys

The largest group. A pattern runs through it: a generic key plus per-provider overrides of the form <key><Provider> (e.g. box.sizeHetzner). The provider-specific form wins over the generic one for that provider. This applies to box.size*, box.image*, and box.defaultCheckpoint*.

Provider

KeyTypeDefaultMeaning
box.providerenum docker/daytona/hetzner/vercel/e2b/digitalocean/remote-dockerdockerbackend new boxes are created on

This is what plain agentbox claude (or create, codex, opencode) uses when you don't pass --provider. agentbox install offers to set it for you at the end of the wizard, so the provider you just logged in to and prepared becomes the one new boxes go to. Set it yourself at any time:

agentbox config set box.provider hetzner --global    # every project on this machine
agentbox config set box.provider hetzner             # this project only

A per-run --provider docker (or the agentbox docker claude prefix) still overrides it.

See local Docker, Remote Docker, Hetzner, Daytona, Vercel, and core concepts.

Resource limits (Docker)

KeyTypeDefaultMeaning
box.memoryint (MiB)0 (unlimited)hard memory ceiling; use --memory on create for byte/k/m/g strings
box.cpusint0 (unlimited)whole-core cap; use --cpus for fractional like 1.5
box.pidsLimitint0 (unlimited)max PID count
box.diskstring (advanced)emptybest-effort writable-layer size; no-op on overlay2 / macOS engines

VM size (cloud)

KeyTypeDefaultMeaning
box.sizestringemptygeneric cloud size, provider-interpreted: hetzner = server type (cx33), digitalocean = Droplet size slug (s-4vcpu-8gb), daytona = cpu-memory-disk GB (4-8-20), vercel = vCPU count (1/2/4/8), e2b = cpu-memory GB (4-8, baked at prepare time); docker ignores it
box.sizeDaytona / box.sizeHetzner / box.sizeDigitalocean / box.sizeVercel / box.sizeE2b / box.sizeRemoteDockerstring (advanced)emptyper-provider override of box.size
box.sizeDockerstring (advanced)emptyreserved — docker uses box.memory / box.cpus / box.disk
box.daytonaClassstringlinux-vmDaytona sandbox class: linux-vm gives a true pause (CPU + memory frozen, running processes survive) and a ~1 min base bake, but runs only in us-east-1. container keeps the old behavior and any region. Changing it needs agentbox prepare --provider daytona --force. Daytona-only
box.daytonaRegionstringemptyDaytona region (us, eu, us-east-1). Empty derives it from the class — linux-vmus-east-1 (the only region with VM runners), container ⇒ the account default. Daytona-only
box.daytonaTimeoutMsint1500000 (25 min)how long a box may sit idle before it's paused; 0 disables. The host enforces this (and holds the box open while the agent is working): Daytona's own timer is an inactivity window that the relay's polling keeps resetting, so it only fires as a backstop when the relay isn't running. See Daytona → idle boxes. Daytona-only
box.daytonaVmBaseImagestring (advanced)emptyregistry image the linux-vm base is baked from. Empty uses the box image AgentBox publishes. Set it when there's no published image for your build context — a locally modified Dockerfile.box — or to bake from a private mirror. Must be amd64 with an explicit tag. Daytona-only
box.hetznerLocationstringnbg1Hetzner datacenter new boxes are created in (nbg1, fsn1, hel1, ash); override per box with --location. Hetzner-only
box.digitaloceanRegionstringnyc3DigitalOcean region new boxes are created in (nyc3, sfo3, ams3, fra1, …); override per box with --location. DigitalOcean-only
box.remoteDockerHoststringemptydefault SSH destination whose Docker engine runs new boxes — an ~/.ssh/config alias or [user@]host[:port]. Override per box with agentbox docker:<host> … or --remote-host. See Remote Docker. remote-docker-only
box.digitaloceanProjectstringemptythe DigitalOcean Project new boxes are placed in — a project name or its UUID. Empty leaves them in the account's default project. Pick it at agentbox digitalocean login, or set it per repo in agentbox.yaml. DigitalOcean-only
box.inboundstringlockedinbound-access policy for the VPS per-box firewall. locked = SSH from your host egress IP only; open = SSH from anywhere (0.0.0.0/0, key-only — reach a box from a phone with the laptop off); a CIDR list (e.g. 203.0.113.5/32) = host egress plus those. Override per box with --inbound or after create with agentbox inbound <box>. Hetzner/DigitalOcean-only. See remote access

Box image

KeyTypeDefaultMeaning
box.imagestring (advanced)agentbox/box:devgeneric image ref; the default is a sentinel cloud backends read as "boot from the prepared base snapshot"
box.imageDocker / box.imageDaytona / box.imageHetzner / box.imageDigitalocean / box.imageVercel / box.imageRemoteDockerstring (advanced)emptyper-provider override; the cloud ones are written by agentbox prepare --provider <name>. Leave box.imageRemoteDocker empty — that provider derives a fingerprint-tagged ref and ensures it on the remote engine itself
box.imageRegistrystring (advanced, docker only)ghcr.io/madarco/agentbox/boxregistry to pull the prebuilt base from before building locally; empty = always build

HEADS UP

Setting the generic box.image to a provider-native snapshot id breaks creates on other providers. Prefer the per-provider box.image<Provider> keys (which is what prepare writes). If a stale box.image blocks creates, clear it with agentbox config unset box.image --project.

Default checkpoint per project

KeyTypeDefaultMeaning
box.defaultCheckpointstringemptycheckpoint new boxes start from when --snapshot isn't given; set via agentbox checkpoint set-default
box.defaultCheckpointDocker / …Daytona / …Hetzner / …Digitalocean / …Vercel / …E2b / …RemoteDockerstring (advanced)emptyper-provider overrides; set via checkpoint set-default --provider <name>

See checkpoints and pausing.

Create-time toggles

KeyTypeDefaultMeaning
box.hostSnapshotboolpromptuse a frozen APFS clone of the host workspace as overlay lower (renamed from box.snapshot)
box.withEnvboolfalsecopy host env/config files (.env*, secrets.toml, agentbox.yaml, …) into /workspace, bypassing gitignore
box.withPlaywrightboolfalseinstall @playwright/cli@latest in the box at create
box.claudeInstallenum native/npmnativehow agentbox prepare installs Claude Code into the base image/snapshot. npm (@anthropic-ai/claude-code) is a fallback for cloud egress IPs the native installer's CDN 403s; bake-time only, so re-run prepare after changing it. Override per-run with prepare --claude-install <mode>
box.vncbooltruerun the per-box Xvnc + noVNC stack
box.autoApproveSafeHostActionsbooltrueauto-approve the safe subset of host actions without a prompt: opening a PR, PR/review comments, re-running CI, pushing to the box's scratch or host-sanctioned branch, checkpoints, integration writes, and file copy/download that stays inside the box project folder (non-secret). Uncontained/secret transfers, non-sanctioned-branch pushes, and PR merge/checkout still prompt. Set false to prompt for every host action. Each bypass is logged as a relay event. See sync & git
box.autoApproveHostActionsboolfalseauto-approve all host-action confirms (the superset: git push, cp, gh PR writes incl. merge/checkout, checkpoint) for this box without a prompt; for unattended orchestration of trusted boxes. Each bypass is logged as a relay event. See orchestration
box.resyncOnStartbooltrueon starting a session, merge the host's current branch + overlay changes (box wins on conflict, warns the agent)
box.bundleDepthintadaptivecap git-bundle history shipped to cloud sandboxes; 0 = full history; ignored for docker
box.dockerCacheSharedboolfalseshare the in-box docker image cache across boxes; only one box can run at a time when set
box.credentialSyncbooltrueautomatically sync refreshed agent credentials from boxes to the host backup and out to all other running boxes (Claude's OAuth refresh rotates the refresh token, killing every other copy). --no-credential-sync at create disables the in-box watcher for that box. See run an agent

See teleport a project, environment, browser and screen, sync and git, and Docker in Docker.

Config-volume isolation

KeyTypeDefaultMeaning
box.isolateClaudeConfig / box.isolateCodexConfig / box.isolateOpencodeConfigboolfalsegive the box its own agent config volume instead of the shared one

Vercel only

KeyTypeDefaultMeaning
box.vercelTimeoutMsint2700000 (45 min)max session length before auto-snapshot; persistent mode auto-resumes
box.vercelNetworkPolicystringempty (allow-all)egress lock: allow-all, deny-all, or a comma-separated domain allowlist (github.com,*.npmjs.org)
box.e2bTimeoutMsint2700000 (45 min)session timeout a new --provider e2b box is created with before E2B auto-pauses it on inactivity; the host keepalive holds it open while the agent works (Hobby caps total session at ~1 h)

See Vercel.

checkpoint.*

KeyTypeDefaultMeaning
checkpoint.maxLayersint (advanced)3max stacked checkpoint layers before a new checkpoint is materialized flattened instead of layered

See checkpoints and pausing.

Agent sessions — claude / codex / opencode

KeyTypeDefaultMeaning
claude.sessionNamestringclaudetmux session name for the claude agent
codex.sessionNamestringcodextmux session name for the codex agent
opencode.sessionNamestringopencodetmux session name for the opencode agent
claude.dangerouslySkipPermissionsbooltruelaunch claude with --dangerously-skip-permissions (auto-accept tool use)
codex.dangerouslySkipPermissionsbooltruelaunch codex with --dangerously-bypass-approvals-and-sandbox

WHY

The permission-skip defaults are on precisely because each box is a throwaway sandbox the agent can't escape — that's the whole point of AgentBox. Turn them off per-box with --no-dangerously-skip-permissions if you want approval prompts. See core concepts and run an agent.

attach

KeyTypeDefaultMeaning
attach.openInenum split/window/tab/samesplitwhere agentbox claude|codex|opencode opens the attached session under tmux, cmux, Herdr, or iTerm2
attach.cmuxStatusbooltruewhen attached inside cmux, reflect the box agent's activity on its cmux workspace (colour + description)
attach.herdrStatusbooltruewhen attached inside Herdr, report the box agent's activity to its Herdr pane (so it looks like a normal agent) and highlight AgentBox's own approval prompts

split uses a tmux split-window / cmux new-split / Herdr pane.split / iTerm2 vertical split (same workspace). tab opens a new tmux window / a new cmux surface (a tab in the current pane, same workspace) / a new Herdr tab / a new iTerm2 tab. window opens a new tmux window / a separate cmux workspace / a new Herdr workspace / a new iTerm2 window. Outside tmux/cmux/Herdr/iTerm2 every value behaves like same. Under Herdr the default is a new tab (rather than a split) — set attach.openIn (or pass --attach-in) explicitly to override. See access your box.

attach.cmuxStatus surfaces a box's agent state in the cmux sidebar. While you're attached inside a cmux surface, AgentBox reflects the agent's live activity on the box's cmux workspace via its colour and description — blue/"working" while the agent runs, amber/"needs input" when it asks a question or is waiting, and the tint clears when idle. The workspace's original colour and description are restored when you detach. When you open several boxes from one project as tabs in the same workspace (--attach-in tab), AgentBox also flags the individual tab whose agent needs input via a cmux notification (tab badge + reorder + a desktop notification), so you can tell which box is waiting; the flag clears when you focus that tab. (cmux only renders its status pills for workspaces running an agent it recognizes; a box runs the agent inside the container, so AgentBox drives the always-visible workspace colour/description and the per-tab highlight instead.) No effect outside cmux; set to false to disable.

attach.herdrStatus makes a box look like a normal agent inside Herdr. While you're attached inside a Herdr pane, AgentBox reports the box agent's live activity to that pane (working / blocked / idle), so Herdr applies its native agent treatment — including its own needs-input handling. The one thing Herdr can't see is AgentBox's own host-relay approval prompts (git push, PR, checkpoint, …), so those get an explicit Herdr notification. No effect outside Herdr; set to false to disable. See Herdr integration.

code / shell

KeyTypeDefaultMeaning
code.ideenum vscode/cursor/autoautowhich IDE agentbox code launches; auto prefers code, falls back to cursor
code.waitbooltrueblock on agentbox-ctl wait-ready before opening the IDE
code.timeoutMsint120000wait-ready timeout
code.autoTerminalsbooltruegenerate /workspace/.vscode/tasks.json so the IDE auto-opens log panels
shell.userstringvscodedefault in-container user for agentbox shell
shell.loginbooltruepass -l to bash (load login profile)
shell.tmuxbooltruerun inside a detachable tmux session (Ctrl+a d to detach)

See access your box.

ssh

KeyTypeDefaultMeaning
ssh.autoConfigbooltrueauto-maintain ~/.agentbox/ssh/config (Include'd from ~/.ssh/config) for SSH-capable cloud boxes

For providers that reach a box over plain SSH with a persistent per-box key (Hetzner, and DigitalOcean once wired), AgentBox writes one Host <box-name> entry per box into its own ~/.agentbox/ssh/config file and adds a single Include ~/.agentbox/ssh/config line to the top of your ~/.ssh/config — so ssh <box-name> (and VS Code Remote-SSH, Codex, Claude desktop) just works without touching the rest of your config. The file is regenerated from box state on create and on start/resume (a cloud box's public IP can change across stop/start), and a box's entry is dropped on destroy. Set ssh.autoConfig to false if you manage ~/.ssh/config yourself; the explicit agentbox shell <box> --ssh-config, agentbox code, and agentbox open commands still write the entry on demand. Docker boxes (no SSH) and Daytona (60-minute token auth, no persistent key) are never auto-added.

browser

KeyTypeDefaultMeaning
browser.defaultenum agent-browser/playwright/bothagent-browserdefault browser stack in the box; playwright or both implies box.withPlaywright

See browser and screen.

integrations

Per-service toggles for relay-gated service integrations. Each integration is disabled by default — even when the host CLI is installed and authed, the box can't call out until you flip it on. The box never holds the service's token; reads pass through, writes prompt on the host. See Notion and Linear.

KeyTypeDefaultMeaning
integrations.notion.enabledboolfalseproxy ntn calls from the box through the host relay; reads pass, writes prompt
integrations.linear.enabledboolfalseproxy linear calls (@schpet/linear-cli) from the box through the host relay; reads pass, writes prompt; auth token is hard-rejected
agentbox config set --project integrations.notion.enabled true
agentbox config set --project integrations.linear.enabled true

agentbox doctor reports a row per integration in a dedicated integrations: group: disabled (default), ntn not installed, not logged in, or authed — with a one-line hint for each non-ok state.

queue & autopause

queue.* schedules background -i jobs; autopause.* pauses idle boxes.

KeyTypeDefaultMeaning
queue.enabledbooltruerun agentbox claude|codex|opencode -i <prompt> jobs through the host-wide FIFO queue
queue.maxConcurrentint5max simultaneously-running boxes before -i jobs queue; override with --max-running <n>
queue.maxWorkingint0 (off)max agents actively working at once before -i jobs queue; override with --max-working <n>
queue.idleGraceSecondsint15debounce before an agent frees its working slot; only used when maxWorking > 0
queue.openInenum none/split/window/tabnonewhen a background -i job's box becomes ready, open an attached terminal onto it (a tmux/cmux/Herdr split, window, or tab); none opens nothing
autopause.enabledbooltruelet the relay periodically pause idle boxes when more than maxRunningBoxes run
autopause.maxRunningBoxesint5target ceiling of running boxes before idle ones get paused
autopause.idleMinutesint5minutes a box must be continuously idle before it's eligible for auto-pause
agentbox config set queue.maxConcurrent 3 --global
agentbox config set autopause.idleMinutes 10 --global
agentbox config set queue.openIn split --global

By default a background -i run just queues and prints its job line. Set queue.openIn to split, window, or tab and the host relay opens an attached terminal onto the box the moment its worker finishes creating it — no need to find the box and attach by hand. It only fires when the submitting shell is inside tmux, cmux, Herdr, or iTerm2. Under cmux, split splits the pane you submitted from (falling back to the parent workspace, then a new workspace), tab adds a tab in the parent workspace, and window opens a separate workspace; under Herdr, split splits the pane you submitted from, tab adds a tab in the parent workspace, and window opens a separate workspace; iTerm2 opens relative to the frontmost window. Unlike attach.openIn there is no same mode (the box is created asynchronously, so it is always a fresh terminal).

cmux: allow socket control

The box is opened by the relay's queue worker, a detached host process — not a cmux-initiated one. cmux's default socketControlMode: cmuxOnly only trusts processes cmux itself started, so it blocks the worker and nothing opens. To use queue.openIn under cmux, set socketControlMode to automation (or password) in ~/.config/cmux/cmux.json and run cmux reload-config. tmux and iTerm2 need no such change.

Auto-pause only ever targets a box whose live agent has settled to idle for idleMinutes. A box where any agent (Claude, Codex, or OpenCode) is working, compacting, or waiting on you is never auto-paused. If a box does get paused while you still need it, agentbox drive …, agentbox shell, and agentbox unpause all resume it on demand (drive auto-unpauses before it reaches the session).

See background and parallel and checkpoints and pausing.

cloud / engine / portless / relay / vnc / maintenance

The remaining infrastructure knobs — mostly advanced.

KeyTypeDefaultMeaning
cloud.useCurrentBranchboolfalseon daytona/hetzner, start boxes on the host's current branch instead of forking agentbox/<box-name>; overridden by --use-branch / --from-branch
engine.kindenum orbstack/docker-desktop/other/autoautooverride docker-engine auto-detection; the one key applied at CLI startup
portless.enabledboolpromptmap each box web app to https://<box-name>.localhost via the Portless proxy (Docker Desktop only)
portless.stateDirstring (advanced)emptyhost Portless state dir to share into boxes
relay.portint (advanced)8787host relay TCP port
relay.controlPlaneUrlstringemptyURL of a deployed control plane; cloud boxes point at it for git-token leasing, permission state, and the registry/events so they keep working with the laptop off. Set via agentbox control-plane set-url
vnc.containerPortint (advanced)6080container-side noVNC port
maintenance.pruneProjectConfigsbooltrueperiodically delete ~/.agentbox/projects/<hash>/ dirs whose source folder is gone
maintenance.pruneProjectConfigsEveryint50run the orphan sweep every N successful agentbox create
update.checkbooltruedaily background check for a newer published CLI (npm) and menu-bar app (release checksum), plus the "newer version available" nudge; at most one network probe per 24h — false disables both

See web apps and tunnels, local Docker, sync and git, and browser and screen.

Where the files live

$ agentbox config path --global
/Users/you/.agentbox/config.yaml
$ agentbox config path --project
/Users/you/.agentbox/projects/3f2a8c1d9e0b4a76/config.yaml
$ agentbox config list-projects
3f2a8c1d9e0b4a76  /Users/you/Projects/myapp
  • Global: ~/.agentbox/config.yaml
  • Per-project user: ~/.agentbox/projects/<sha1-16-of-abs-path>/config.yaml
  • Workspace (committed): the defaults: block in agentbox.yaml

For the defaults: block and the full agentbox.yaml schema, see agentbox.yaml; for secrets and env files, see environment. The served JSON schema lives at /schema/user-config.schema.json.

On this page