FastAPI control plane for OpenSandbox that manages sandbox lifecycle on Docker (ready) and Kubernetes (planned) runtimes.
Project description
OpenSandbox Server
English | 中文
A production-grade, FastAPI-based service for managing the lifecycle of containerized sandboxes. It acts as the control plane to create, run, monitor, and dispose isolated execution environments across container platforms.
Features
Core capabilities
- Lifecycle APIs: Standardized REST interfaces for create, start, pause, resume, delete
- Pluggable runtimes:
- Docker: Production-ready
- Kubernetes: Supported (see
kubernetes/for deployment)
- Automatic expiration: Configurable TTL with renewal
- Access control: API Key authentication (
OPEN-SANDBOX-API-KEY); can be disabled for local/dev - Networking modes:
- Host: shared host network, performance first
- Bridge: isolated network with built-in HTTP routing
- Resource quotas: CPU/memory limits with Kubernetes-style specs
- Observability: Unified status with transition tracking
- Registry support: Public and private images
Extended capabilities
- Async provisioning: Background creation to reduce latency
- Timer restoration: Expiration timers restored after restart
- Env/metadata injection: Per-sandbox environment and metadata
- Port resolution: Dynamic endpoint generation
- Structured errors: Standard error codes and messages
Requirements
- Python: 3.10 or higher
- Package Manager: uv (recommended) or pip
- Runtime Backend:
- Docker Engine 20.10+ (for Docker runtime)
- Kubernetes 1.21+ (for Kubernetes runtime)
- Operating System: Linux, macOS, or Windows with WSL2
Quick Start
Installation
- Install from PyPI:
For source development or contributions, you can still clone the repo and run
uv syncinsideserver/.uv pip install opensandbox-server
Configuration
The server uses a TOML configuration file to select and configure the underlying runtime.
Init configuration from simple example:
# run opensandbox-server -h for help
opensandbox-server init-config ~/.sandbox.toml --example docker
Create K8S configuration file
The K8S version of the Sandbox Operator needs to be deployed in the cluster, refer to the Kubernetes directory.
# run opensandbox-server -h for help
opensandbox-server init-config ~/.sandbox.toml --example k8s
[optional] Edit configuration for your environment
- For quick e2e/demo (specify which one):
opensandbox-server init-config ~/.sandbox.toml --example docker # or docker-zh|k8s|k8s-zh # add --force to overwrite existing file
- Render the full schema-driven skeleton (no defaults, just placeholders) by omitting --example:
opensandbox-server init-config ~/.sandbox.toml # add --force to overwrite existing file
[optional] Edit ~/.sandbox.toml for your environment
Before you start the server, edit the configuration file to suit your environment. You could also generate a new empty configuration file by opensandbox-server init-config ~/.sandbox.toml.
Docker runtime + host networking
[server]
host = "0.0.0.0"
port = 8080
log_level = "INFO"
api_key = "your-secret-api-key-change-this"
[runtime]
type = "docker"
execd_image = "opensandbox/execd:v1.0.6"
[docker]
network_mode = "host" # Containers share host network; only one sandbox instance at a time
Docker runtime + bridge networking
[server]
host = "0.0.0.0"
port = 8080
log_level = "INFO"
api_key = "your-secret-api-key-change-this"
[runtime]
type = "docker"
execd_image = "opensandbox/execd:v1.0.6"
[docker]
network_mode = "bridge" # Isolated container networking
Security hardening (applies to all Docker modes)
[docker]
# Drop dangerous capabilities and block privilege escalation by default
drop_capabilities = ["AUDIT_WRITE", "MKNOD", "NET_ADMIN", "NET_RAW", "SYS_ADMIN", "SYS_MODULE", "SYS_PTRACE", "SYS_TIME", "SYS_TTY_CONFIG"]
no_new_privileges = true
apparmor_profile = "" # e.g. "docker-default" when AppArmor is available
# Limit fork bombs and optionally enforce seccomp / read-only rootfs
pids_limit = 512 # set to null to disable
seccomp_profile = "" # path or profile name; empty uses Docker default
Further reading on Docker container security: https://docs.docker.com/engine/security/
Ingress exposure (direct | gateway)
[ingress]
mode = "direct" # docker runtime only supports direct
# gateway.address = "*.example.com" # host only (domain or IP[:port]); scheme is not allowed
# gateway.route.mode = "wildcard" # wildcard | uri | header
mode=direct: default; required whenruntime.type=docker(client ↔ sandbox direct reachability, no L7 gateway).mode=gateway: configure external ingress.gateway.address: wildcard domain required whengateway.route.mode=wildcard; otherwise must be domain, IP, or IP:port. Do not include scheme; clients decide http/https.gateway.route.mode:wildcard(host-based wildcard),uri(path-prefix),header(header-based routing).- Response format examples:
wildcard:<sandbox-id>-<port>.example.com/path/to/requesturi:10.0.0.1:8000/<sandbox-id>/<port>/path/to/requestheader:gateway.example.comwith headerOpenSandbox-Ingress-To: <sandbox-id>-<port>
Kubernetes runtime
[runtime]
type = "kubernetes"
execd_image = "opensandbox/execd:v1.0.5"
[kubernetes]
kubeconfig_path = "~/.kube/config"
namespace = "opensandbox"
workload_provider = "batchsandbox" # or "agent-sandbox"
informer_enabled = true # Beta: enable watch-based cache
informer_resync_seconds = 300 # Beta: full list interval
informer_watch_timeout_seconds = 60 # Beta: watch restart interval
- Informer settings are beta and enabled by default to reduce API calls; set
informer_enabled = falseto turn off. - Resync and watch timeouts control how often the cache refreshes; tune for your cluster API limits.
Egress sidecar for networkPolicy
- Required when using
networkPolicy: Configure the sidecar image. Theegress.imagesetting is mandatory when requests includenetworkPolicy:[runtime] type = "docker" execd_image = "opensandbox/execd:v1.0.6" [egress] image = "opensandbox/egress:v1.0.0"
- Supported only in Docker bridge mode; requests with
networkPolicyare rejected whennetwork_mode=hostor whenegress.imageis not configured. - Main container shares the sidecar netns and explicitly drops
NET_ADMIN; the sidecar keepsNET_ADMINto manage iptables. - IPv6 is disabled in the shared namespace when the egress sidecar is injected to keep policy enforcement consistent.
- Sidecar image is pulled before start; delete/expire/failure paths attempt to clean up the sidecar as well.
- Request example (
CreateSandboxRequestwithnetworkPolicy):{ "image": {"uri": "python:3.11-slim"}, "entrypoint": ["python", "-m", "http.server", "8000"], "timeout": 3600, "resourceLimits": {"cpu": "500m", "memory": "512Mi"}, "networkPolicy": { "defaultAction": "deny", "egress": [ {"action": "allow", "target": "pypi.org"}, {"action": "allow", "target": "*.python.org"} ] } }
- When
networkPolicyis empty or omitted, no sidecar is injected (allow-all at start).
Run the server
Start the server using the installed CLI (reads ~/.sandbox.toml by default):
opensandbox-server
The server will start at http://0.0.0.0:8080 (or your configured host/port).
Run the server (installed package)
After installing the package (wheel or PyPI), you can use the CLI entrypoint:
opensandbox-server --config ~/.sandbox.toml
Health check
curl http://localhost:8080/health
Expected response:
{"status": "healthy"}
API documentation
Once the server is running, interactive API documentation is available:
- Swagger UI: http://localhost:8080/docs
- ReDoc: http://localhost:8080/redoc
Further reading on Docker container security: https://docs.docker.com/engine/security/
API authentication
Authentication is enforced only when server.api_key is set. If the value is empty or missing, the middleware skips API Key checks (intended for local/dev). For production, always set a non-empty server.api_key and send it via the OPEN-SANDBOX-API-KEY header.
All API endpoints (except /health, /docs, /redoc) require authentication via the OPEN-SANDBOX-API-KEY header when authentication is enabled:
curl http://localhost:8080/v1/sandboxes
Example usage
Create a Sandbox
curl -X POST "http://localhost:8080/v1/sandboxes" \
-H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"image": {
"uri": "python:3.11-slim"
},
"entrypoint": [
"python",
"-m",
"http.server",
"8000"
],
"timeout": 3600,
"resourceLimits": {
"cpu": "500m",
"memory": "512Mi"
},
"env": {
"PYTHONUNBUFFERED": "1"
},
"metadata": {
"team": "backend",
"project": "api-testing"
}
}'
Response:
{
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"status": {
"state": "Pending",
"reason": "CONTAINER_STARTING",
"message": "Sandbox container is starting.",
"lastTransitionAt": "2024-01-15T10:30:00Z"
},
"metadata": {
"team": "backend",
"project": "api-testing"
},
"expiresAt": "2024-01-15T11:30:00Z",
"createdAt": "2024-01-15T10:30:00Z",
"entrypoint": ["python", "-m", "http.server", "8000"]
}
Get Sandbox Details
curl -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \
http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab
Get Service Endpoint
curl -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \
http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/endpoints/8000
# execd (agent) endpoint
curl -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \
http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/endpoints/44772
Response:
{
"endpoint": "sandbox.example.com/a1b2c3d4-5678-90ab-cdef-1234567890ab/8000"
}
Renew Expiration
curl -X POST "http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/renew-expiration" \
-H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"expiresAt": "2024-01-15T12:30:00Z"
}'
Delete a Sandbox
curl -X DELETE \
-H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \
http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab
Architecture
Component responsibilities
- API Layer (
src/api/): HTTP request handling, validation, and response formatting - Service Layer (
src/services/): Business logic for sandbox lifecycle operations - Middleware (
src/middleware/): Cross-cutting concerns (authentication, logging) - Configuration (
src/config.py): Centralized configuration management - Runtime Implementations: Platform-specific sandbox orchestration
Sandbox lifecycle states
create()
│
▼
┌─────────┐
│ Pending │────────────────────┐
└────┬────┘ │
│ │
│ (provisioning) │
▼ │
┌─────────┐ pause() │
│ Running │───────────────┐ │
└────┬────┘ │ │
│ resume() │ │
│ ┌────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────┐ │
├─│ Paused │ │
│ └────────┘ │
│ │
│ delete() or expire() │
▼ │
┌──────────┐ │
│ Stopping │ │
└────┬─────┘ │
│ │
├────────────────┬────────┘
│ │
▼ ▼
┌────────────┐ ┌────────┐
│ Terminated │ │ Failed │
└────────────┘ └────────┘
Configuration reference
Server configuration
| Key | Type | Default | Description |
|---|---|---|---|
server.host |
string | "0.0.0.0" |
Interface to bind |
server.port |
integer | 8080 |
Port to listen on |
server.log_level |
string | "INFO" |
Python logging level |
server.api_key |
string | null |
API key for authentication |
Runtime configuration
| Key | Type | Required | Description |
|---|---|---|---|
runtime.type |
string | Yes | Runtime implementation ("docker" or "kubernetes") |
runtime.execd_image |
string | Yes | Container image with execd binary |
Egress configuration
| Key | Type | Required | Description |
|---|---|---|---|
egress.image |
string | Required when using networkPolicy |
Container image with egress binary. Must be configured when networkPolicy is provided in sandbox creation requests. |
Docker configuration
| Key | Type | Default | Description |
|---|---|---|---|
docker.network_mode |
string | "host" |
Network mode ("host" or "bridge") |
Agent-sandbox configuration
| Key | Type | Default | Description |
|---|---|---|---|
agent_sandbox.template_file |
string | null |
Sandbox CR YAML template for agent-sandbox (used when kubernetes.workload_provider = "agent-sandbox") |
agent_sandbox.shutdown_policy |
string | "Delete" |
Shutdown policy on expiry ("Delete" or "Retain") |
agent_sandbox.ingress_enabled |
boolean | true |
Whether ingress routing is expected to be enabled |
Environment variables
| Variable | Description |
|---|---|
SANDBOX_CONFIG_PATH |
Override config file location |
DOCKER_HOST |
Docker daemon URL (e.g., unix:///var/run/docker.sock) |
DOCKER_API_TIMEOUT |
Docker client timeout in seconds (default: 180) |
PENDING_FAILURE_TTL |
TTL for failed pending sandboxes in seconds (default: 3600) |
Development
Code quality
Run linter:
uv run ruff check
Auto-fix issues:
uv run ruff check --fix
Format code:
uv run ruff format
Testing
Run all tests:
uv run pytest
Run with coverage:
uv run pytest --cov=src --cov-report=html
Run specific test:
uv run pytest tests/test_docker_service.py::test_create_sandbox_requires_entrypoint
License
This project is licensed under the terms specified in the LICENSE file in the repository root.
Contributing
Contributions are welcome. Suggested flow:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests for new functionality
- Ensure all tests pass (
uv run pytest) - Run linting (
uv run ruff check) - Commit with clear messages
- Push to your fork
- Open a Pull Request
Support
- Documentation: See
DEVELOPMENT.mdfor development guidance - Issues: Report defects via GitHub Issues
- Discussions: Use GitHub Discussions for Q&A and ideas
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file opensandbox_server-0.1.3.tar.gz.
File metadata
- Download URL: opensandbox_server-0.1.3.tar.gz
- Upload date:
- Size: 73.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
660c1d26a60097fd54c467fc4ab20a726f0c4a20e7967aaba562b7e48073af91
|
|
| MD5 |
0a195d66aae5d745b861756e422f1e7d
|
|
| BLAKE2b-256 |
9faf29ed1fc4c3e4840e1fabbba813956e470ba93c81adc51ff8d7f515c95bec
|
File details
Details for the file opensandbox_server-0.1.3-py3-none-any.whl.
File metadata
- Download URL: opensandbox_server-0.1.3-py3-none-any.whl
- Upload date:
- Size: 93.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28a242c53fa90bc470fbbb5d0c21821c24cbba369124aad4cdab95f1dc3124fe
|
|
| MD5 |
de5a75d4d7f5428332cc5d9f4d041b2f
|
|
| BLAKE2b-256 |
3254805092f3612bcca1dde6dc92d887dd79d6ef6a153c52040a5c53f0b81961
|