OpenSandbox Server configuration reference
May 18, 2026 · View on GitHub
This document describes all TOML configuration options accepted by the OpenSandbox lifecycle server (opensandbox-server). The schema is defined in opensandbox_server/config.py (AppConfig and nested models).
- Default config path:
~/.sandbox.toml - Override path: set environment variable
SANDBOX_CONFIG_PATHto an absolute or user-expandable path. - CLI:
opensandbox-server --config /path/to/sandbox.tomlalso setsSANDBOX_CONFIG_PATHfor that process.
Example files in this repository:
| File | Purpose |
|---|---|
example.config.toml | Docker runtime (English) |
example.config.zh.toml | Docker runtime (中文) |
example.config.k8s.toml | Kubernetes runtime (English) |
example.config.k8s.zh.toml | Kubernetes runtime (中文) |
Table of contents
- Top-level sections
[server][log][runtime][docker][kubernetes][agent_sandbox][ingress][egress][storage][store][secure_runtime][renew_intent]- Environment variables (outside TOML)
- Cross-field validation rules
Top-level sections
| Section | Required | When |
|---|---|---|
[server] | No | Always (defaults apply if omitted) |
[log] | No | Always (defaults apply if omitted) |
[runtime] | Yes | Always |
[docker] | No | runtime.type = "docker" |
[kubernetes] | No | runtime.type = "kubernetes" (defaults are applied if missing) |
[agent_sandbox] | No | Only when kubernetes.workload_provider = "agent-sandbox" |
[ingress] | No | Optional; see Ingress |
[egress] | No | Required values when clients use networkPolicy on create |
[storage] | No | Host bind mounts / OSSFS mount root |
[store] | No | Server-managed persistent metadata backend |
[secure_runtime] | No | gVisor / Kata / Firecracker |
[renew_intent] | No | Experimental auto-renew on access |
[server] — Lifecycle API
| Key | Type | Default | Description |
|---|---|---|---|
host | string | "0.0.0.0" | Bind address for the HTTP API. |
port | integer | 8080 | Listen port (1–65535). |
api_key | string | omitted | null | If set to a non-empty string, requests must send header OPEN-SANDBOX-API-KEY with this value (except documented public routes such as /health, /docs, /redoc). If omitted or empty, API key checks are skipped, but startup now requires explicit risk acknowledgment: interactive TTY confirmation (YES) or OPENSANDBOX_INSECURE_SERVER=YES. |
eip | string | omitted | null | Public IP or hostname used as the host part when the server returns sandbox endpoint URLs (notably Docker runtime). |
max_sandbox_timeout_seconds | integer | omitted | null | Upper bound on sandbox TTL in seconds for create requests that specify timeout. Must be ≥ 60 if set. Omit to disable the server-side cap. |
timeout_keep_alive | integer | 30 | Idle keep-alive timeout (seconds) passed to uvicorn. |
limit_concurrency | integer | 1024 | Maximum concurrent connections before returning 503. Provides backpressure protection under burst load. Set to 0 to disable the cap (TOML cannot express null). |
backlog | integer | 2048 | Socket listen backlog passed to uvicorn. |
thread_pool_size | integer | 200 | Maximum size of the anyio default threadpool used by FastAPI to run sync route handlers. The anyio default of 40 throttles bursts of blocking sandbox list/get/delete operations under high concurrency. |
loop | "auto" | "uvloop" | "asyncio" | "auto" | Event loop implementation. auto prefers uvloop and falls back to asyncio. |
http | "auto" | "httptools" | "h11" | "auto" | HTTP protocol parser. auto prefers httptools and falls back to h11. |
[log]
| Key | Type | Default | Description |
|---|---|---|---|
level | string | "INFO" | Python logging level for the server process (e.g. "DEBUG", "INFO", "WARNING"). |
file_enabled | boolean | false | When true, logs are written to rotating files instead of stdout. |
file_path | string | omitted | null | Override path for the main log file. Defaults to ~/logs/opensandbox/server.log when file_enabled = true. |
access_file_path | string | omitted | null | Override path for the HTTP access log file. Defaults to ~/logs/opensandbox/access.log when file_enabled = true. |
file_max_bytes | integer | 104857600 (100 MB) | Max bytes per log file before rotation. |
file_backup_count | integer | 5 | Number of rotated log files to retain. |
[runtime] — required
| Key | Type | Default | Description |
|---|---|---|---|
type | string | — | docker or kubernetes. Selects which runtime implementation loads. |
execd_image | string | — | OCI image containing the execd binary used to bootstrap command/file access inside the sandbox. |
[docker] — only when runtime.type = "docker"
| Key | Type | Default | Description |
|---|---|---|---|
network_mode | string | "host" | Docker network attachment for sandbox containers: host, bridge, or a custom user-defined network name. Egress sidecar + networkPolicy require bridge (see Egress). |
api_timeout | integer | omitted | null | Docker API timeout in seconds. If unset, the code uses default 180 s where applicable. |
host_ip | string | omitted | null | Hostname or IP used when rewriting bridge-mode endpoint URLs (e.g. server runs in Docker and clients need a host-reachable address). Often host.docker.internal or the host LAN IP on Linux. |
drop_capabilities | list of strings | See config.py | Linux capabilities dropped from sandbox containers (security hardening). |
apparmor_profile | string | omitted | null | Optional AppArmor profile name (e.g. "docker-default"). Empty/unset lets Docker use its default. |
no_new_privileges | boolean | true | Sets no-new-privileges to block privilege escalation. |
seccomp_profile | string | omitted | null | Seccomp profile name or absolute path; empty uses Docker default seccomp. |
pids_limit | integer | null | 4096 | Max PIDs per sandbox container; set to null to disable the limit. |
[kubernetes] — only when runtime.type = "kubernetes"
If runtime.type = "kubernetes" and the [kubernetes] table is absent, the server instantiates defaults from KubernetesRuntimeConfig.
| Key | Type | Default | Description |
|---|---|---|---|
kubeconfig_path | string | omitted | null | Path to kubeconfig (expandable, e.g. ~/.kube/config). In-cluster configs often leave this unset and rely on in-cluster credentials. |
namespace | string | omitted | null | Namespace for sandbox workloads. |
service_account | string | omitted | null | ServiceAccount name bound to workload pods. |
workload_provider | string | omitted | null | One of: batchsandbox, agent-sandbox. If omitted, the first registered provider is used (currently batchsandbox). |
batchsandbox_template_file | string | omitted | null | Path to BatchSandbox CR YAML template when workload_provider = "batchsandbox". |
image_pull_policy | string | omitted | "IfNotPresent" | Image pull policy for the BatchSandbox main container. Values: Always, IfNotPresent, Never. |
sandbox_create_timeout_seconds | integer | 60 | Max time to wait for a new sandbox to become ready (e.g. IP assigned), in seconds. |
sandbox_create_poll_interval_seconds | float | 1.0 | Poll interval while waiting for readiness. |
snapshot_create_timeout_seconds | integer | 900 | Max time to wait for a Kubernetes public snapshot to become ready, in seconds. Set this greater than the controller snapshot commitJobTimeout / --commit-job-timeout. |
informer_enabled | boolean | true | [Beta] Use informer/watch cache for reads to reduce API load. |
informer_resync_seconds | integer | 300 | [Beta] Full resync period for the informer cache. |
informer_watch_timeout_seconds | integer | 60 | [Beta] Watch stream restart interval. |
read_qps | float | 0 | K8s API get/list rate limit (QPS). 0 = unlimited. |
read_burst | integer | 0 | Burst for read limiter; 0 means use read_qps as burst (minimum 1 internally). |
write_qps | float | 0 | K8s API write rate limit (QPS). 0 = unlimited. |
write_burst | integer | 0 | Burst for write limiter. |
execd_init_resources | table | omitted | null | Optional resource requests/limits for the execd init container. |
BatchSandbox vs agent-sandbox
Kubernetes workloads are created by a workload provider. There is no [batchsandbox] section in TOML — BatchSandbox is configured entirely under [kubernetes], plus shared sections like [egress], [ingress], [storage], [secure_runtime].
| BatchSandbox (default provider) | agent-sandbox (kubernetes-sigs/agent-sandbox) | |
|---|---|---|
kubernetes.workload_provider | "batchsandbox" or omit (factory default is batchsandbox) | "agent-sandbox" |
| Template file | kubernetes.batchsandbox_template_file — path to BatchSandbox CR YAML | agent_sandbox.template_file in [agent_sandbox] |
| Image pull policy | kubernetes.image_pull_policy — writes imagePullPolicy into the BatchSandbox pod template main container | Not currently used |
| Extra TOML table | None | [agent_sandbox] is required (see below) |
BatchSandbox-only config keys in config.py: batchsandbox_template_file and image_pull_policy on KubernetesRuntimeConfig. Everything else in the [kubernetes] table (namespace, kubeconfig, informer, API QPS, sandbox_create_*, execd_init_resources, …) applies to whichever provider you select.
kubernetes.execd_init_resources
| Key | Type | Description |
|---|---|---|
limits | map string → string | e.g. { cpu = "100m", memory = "128Mi" } |
requests | map string → string | e.g. { cpu = "50m", memory = "64Mi" } |
[agent_sandbox] — only with kubernetes.workload_provider = "agent-sandbox"
Used with the kubernetes-sigs/agent-sandbox Sandbox CRD provider.
| Key | Type | Default | Description |
|---|---|---|---|
template_file | string | omitted | null | Path to Sandbox CR YAML template. |
shutdown_policy | string | "Delete" | Delete or Retain when the sandbox expires. |
ingress_enabled | boolean | true | Whether ingress routing to agent-sandbox pods is expected. |
[ingress]
Controls how ingress exposure is described for sandbox endpoints (especially behind gateways). When runtime.type = "docker", only mode = "direct" is allowed.
secureAccess is currently supported only for Kubernetes sandboxes when ingress.mode = "gateway".
| Key | Type | Default | Description |
|---|---|---|---|
mode | string | "direct" | direct — clients reach sandboxes without an L7 gateway configured here. gateway — use [ingress.gateway] for address and routing mode (Kubernetes-oriented deployments). |
When mode = "gateway"
You must set [ingress.gateway] and omit gateway when mode = "direct".
| Key | Type | Description |
|---|---|---|
address | string | Gateway host (no http:// or https://). For route.mode = "wildcard", must be a wildcard domain (e.g. *.example.com). Otherwise a normal domain, IP, or IP:port. |
route.mode | string | wildcard — host-based routing; uri — path-prefix routing; header — header-based routing. |
Response URL shapes depend on route.mode (see server README / ingress component docs).
[egress]
Configures the egress sidecar image and enforcement mode. The server only attaches the sidecar when a sandbox is created with a networkPolicy in the API request.
| Key | Type | Default | Description |
|---|---|---|---|
image | string | omitted | null | OCI image for the egress sidecar. Required in config when clients send networkPolicy (create request). |
mode | string | "dns" | Passed to the sidecar as OPENSANDBOX_EGRESS_MODE. Values: dns — DNS-proxy-based enforcement (CIDR/static IP rules not enforced); dns+nft — adds nftables where available so CIDR/IP rules can be enforced. |
disable_ipv6 | bool | true | IPv6 egress is incomplete (especially on Kubernetes). Default on; set false only when you want IPv6 left up in the netns. Details in IPv6 and egress below. |
IPv6 and egress
OpenSandbox egress does not treat IPv6 as a first-class, fully covered path—gaps show up most often under runtime.type = "kubernetes" (pod networking, CNI). The default disable_ipv6 = true matches the usual need on dual-stack CNI: do not rely on incomplete IPv6 egress. Set false when the cluster is effectively IPv4-only and you deliberately want IPv6 enabled in the sandbox network namespace, or when you accept those gaps for experiments.
Docker notes:
egress.imagemust be set when usingnetworkPolicy.- Outbound policy requires
docker.network_mode = "bridge";networkPolicyis rejected for incompatible network modes.
Kubernetes notes:
- When
networkPolicyis set, the workload includes an egress sidecar built fromegress.image.
See components/egress/README.md for sidecar behavior and limits.
[storage]
Host-side storage related to volume mounts (host bind allowlist and OSSFS mount layout).
| Key | Type | Default | Description |
|---|---|---|---|
allowed_host_paths | list of strings | [] | Absolute path prefixes allowed for host bind mounts. If empty, all host bind mounts are rejected (secure-by-default). |
ossfs_mount_root | string | "/mnt/ossfs" | Host directory under which OSSFS-backed mounts are resolved (<root>/<bucket>/...). |
volume_default_size | string | "1Gi" | Default storage size for auto-created Kubernetes PVCs when the caller does not specify a size in the PVC provisioning hints. |
Sandbox volume models (host, pvc, ossfs) in API requests are documented in the OpenAPI specs and OSEPs; this table only covers server storage settings.
[store]
Configures the persistence backend for server-managed resources. This is a server-wide store, not a snapshot-specific backend. Snapshot metadata is the first resource persisted here; future persistent server resources should reuse the same backend.
| Key | Type | Default | Description |
|---|---|---|---|
type | string | "sqlite" | Server persistence backend type. Currently only sqlite is supported. |
path | string | "~/.opensandbox/opensandbox.db" | Filesystem path to the SQLite database file used for server-managed metadata. Parent directories are created automatically when needed. |
Notes
- The default SQLite backend gives local and single-node deployments persistent metadata without requiring an external database service.
memoryis intentionally not the default because server-managed snapshot resources must survive process restarts.- Higher-level components should depend on repository abstractions rather than
importing
sqlite3directly.
[secure_runtime]
Optional strong isolation runtimes (gVisor, Kata, Firecracker).
| Key | Type | Default | Description |
|---|---|---|---|
type | string | "" | "" — default OCI runtime (runc). gvisor, kata, firecracker. firecracker is Kubernetes-only. |
docker_runtime | string | omitted | null | Docker OCI runtime name (e.g. runsc for gVisor, kata-runtime for Kata). |
k8s_runtime_class | string | omitted | null | Kubernetes RuntimeClass name (e.g. gvisor, kata-qemu, kata-fc). |
Validation (summary):
- If
typeis empty,docker_runtimeandk8s_runtime_classmust be omitted. - If
typeisfirecracker,k8s_runtime_classis required (dockerruntime cannot use Firecracker). - If
typeisgvisororkata, at least one ofdocker_runtimeork8s_runtime_classmust be set.
See docs/secure-container.md for installation and node requirements.
[renew_intent] — experimental
🧪 Experimental: auto-renew sandbox expiration when access is observed (lifecycle proxy and/or Redis queue). Off by default. Full design: OSEP-0009.
Use dotted keys under the same table for Redis (valid in TOML):
| Key | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Master switch for renew-on-access. |
min_interval_seconds | integer | 60 | Minimum seconds between renewals for the same sandbox (cooldown). ≥ 1. |
redis.enabled | boolean | false | Enable Redis list consumer for ingress-gateway renew intents. |
redis.dsn | string | omitted | null | Redis URL, e.g. redis://127.0.0.1:6379/0. Required when redis.enabled = true. |
redis.queue_key | string | "opensandbox:renew:intent" | Redis list key for renew-intent payloads. |
redis.consumer_concurrency | integer | 8 | Concurrent BRPOP workers (≥ 1). |
Per-sandbox enablement uses create request extensions (see OSEP-0009 and example.config.toml comments).
Environment variables (outside TOML)
These are read by the server or runtime code in addition to the TOML file:
| Variable | Where used | Description |
|---|---|---|
SANDBOX_CONFIG_PATH | config.py, CLI | Path to the TOML file. Overrides the default ~/.sandbox.toml when set. |
OPENSANDBOX_SERVER_API_KEY | config.py | Overrides the API key from the TOML file. |
DOCKER_HOST | Docker service | Standard Docker daemon address (e.g. unix:///var/run/docker.sock). |
PENDING_FAILURE_TTL | Docker service | Seconds to retain failed Pending sandboxes before cleanup; default 3600. |
Cross-field validation rules
Rules enforced when the full AppConfig is parsed (see AppConfig.validate_runtime_blocks in config.py):
-
runtime.type = "docker"- Must not include
[kubernetes]or[agent_sandbox]. - If
[ingress]is present,ingress.modemust be"direct". secure_runtime.type = "firecracker"is not allowed.
- Must not include
-
runtime.type = "kubernetes"[kubernetes]is created with defaults if missing.[agent_sandbox]is only allowed whenkubernetes.workload_provider(case-insensitive) isagent-sandbox.
-
ingress.mode = "gateway"[ingress.gateway]is required; address androute.modemust satisfy the validators (wildcard domain forwildcardroute mode, no URL scheme inaddress, etc.).
-
secure_runtime- See Secure runtime above.
Source of truth
If this document and the running server disagree, prefer:
opensandbox_server/config.py— authoritative Pydantic schema and defaults.- Example TOML files in the
server/directory — reviewed snapshots for Docker/K8s. - Release notes — for experimental flags and breaking changes.
For API request fields (create sandbox, networkPolicy, volumes, etc.), see the OpenAPI specs under specs/ and the main Server README.