Sandbox
Overview
uf sandbox provides containerized development sessions for AI agents. Instead of running OpenCode directly on your host, the sandbox creates an isolated session — either an ephemeral Podman container or a persistent DevPod workspace — with your project mounted, OpenCode installed, and API keys forwarded. The agent works inside the container where file changes are isolated (or directly applied, depending on mount mode).
Prerequisites depend on which sandbox mode you use:
- Ephemeral containers: Podman must be installed. On macOS, a Podman machine must be running (
podman machine start). - DevPod persistent workspaces (provisioned via
uf sandbox create): Runuf setup, which installs DevPod and registers a docker-type provider under the namepodman(viaDOCKER_PATH=podman). You do not need a standalonepodmanbinary in your$PATH— the configured provider handles container runtime resolution automatically.
uf sandbox start auto-detects which mode to use: if a persistent workspace exists, it resumes it; otherwise it launches an ephemeral container.
Run uf doctor to verify Podman runtime health, Docker-to-Podman shim, DevPod version (>= 0.5.0), and DevPod provider configuration. If devpod up fails, the CLI displays a diagnostic hint: run uf doctor to diagnose or uf setup to configure the provider.
Session Management
sandbox start
Launch a containerized OpenCode session. If a persistent workspace exists (from uf sandbox create), resumes it. Otherwise, starts an ephemeral container.
uf sandbox start # ephemeral, isolated mode
uf sandbox start --mode direct # read-write mounts
uf sandbox start --detach # start without attaching
uf sandbox start --image my-image # custom container image| Flag | Description |
|---|---|
--mode <string> | Mount mode: isolated (read-only, default) or direct (read-write) |
--detach | Start container without attaching the TUI |
--image <string> | Container image (default from UF_SANDBOX_IMAGE or quay.io/unbound-force/opencode-dev:latest) |
--memory <string> | Container memory limit (default 8g) |
--cpus <string> | Container CPU limit (default 4) |
--backend <string> | Backend: auto, podman, or che (default auto) |
--ide <string> | IDE for DevPod to open after start: none (default), vscode, openvscode, fleet, jupyternotebook, cursor |
--uidmap | Use explicit UID/GID mapping (macOS escape hatch) |
--provider <string> | DevPod provider name (default podman). References a registered DevPod provider — does not require the binary in PATH. Registered automatically by uf setup |
--no-parent | Mount only the project directory (disable parent directory mount) |
sandbox stop
Stop the running sandbox. For persistent workspaces, state is preserved. For ephemeral containers, the container is removed.
uf sandbox stopsandbox attach
Connect to a running sandbox’s OpenCode TUI. Detects persistent workspaces first, then falls back to ephemeral containers. Requires the sandbox to be running.
uf sandbox attachsandbox extract
Generate a patch from the container’s git history and apply it to the host repo. Uses git format-patch / git am for commit-preserving round-trip extraction.
uf sandbox extract # interactive confirmation
uf sandbox extract --yes # skip confirmation| Flag | Description |
|---|---|
--yes | Skip the confirmation prompt |
sandbox status
Display the current sandbox state: workspace name, backend, image, state, project directory, server URL, demo endpoints, and uptime.
uf sandbox statusMount Modes
The sandbox supports two mount modes that control how your project files are shared with the container:
Isolated Mode (default)
uf sandbox start --mode isolatedYour project is mounted read-only inside the container. The agent can read your code but changes are written to a container overlay. To get changes back to the host, use uf sandbox extract which generates git patches.
When to use: Default for most development. Provides the strongest isolation — the agent cannot accidentally modify or delete host files. Use extract to review and apply changes selectively.
Direct Mode
uf sandbox start --mode directYour project is mounted read-write. Changes the agent makes inside the container are immediately visible on the host filesystem.
When to use: When you need real-time file synchronization (e.g., running tests on the host while the agent edits in the container). Provides less isolation — the agent can modify any mounted file directly.
UID Mapping
Podman’s rootless containers run as a non-root user, but file ownership can mismatch between the container and host. The sandbox handles this automatically using --userns=keep-id:uid=1000,gid=1000, which maps the container’s user (UID 1000) to your host user.
Requirements: Podman >= 4.3 (for keep-id support).
macOS Setup
On macOS, the Podman machine must be configured for virtiofs UID mapping. If you see files appearing as root:nobody inside the container, the machine’s volume driver needs updating.
Use the --uidmap flag as an escape hatch when the Podman machine’s virtiofs does not support --userns=keep-id:
uf sandbox start --uidmapThis uses explicit UID/GID mapping instead of the namespace-based approach.
Troubleshooting: Files Appear as root:nobody
If files inside the sandbox appear owned by root:nobody, the UID mapping is not working. Common causes:
- Podman version too old: Upgrade to Podman >= 4.3
- macOS Podman machine: Restart the machine or use
--uidmap - Linux: Check that
/etc/subuidand/etc/subgidhave entries for your user
Devcontainer Configuration
The sandbox uses a .devcontainer/devcontainer.json file to configure the development container. This file is gitignored — each developer generates their own via uf sandbox init, because the configuration is OS-specific.
OS-Specific UID Mapping
The generated devcontainer config differs by platform:
- macOS: Uses
--userns=keep-id:uid=1000,gid=1000— the Podman VM requires explicit UID/GID values to map through the virtiofs layer - Linux: Uses
--userns=keep-id(without explicit UID/GID) — avoids subuid range conflicts that can occur on container restart when explicit mappings are used
This is why the devcontainer is generated per-developer rather than committed to the repo: macOS and Linux developers need different runArgs to get correct file ownership inside the container.
Note:
uf initdoes not deploy a devcontainer template. Runuf sandbox initto generate the platform-appropriate configuration.
Persistent Workspaces
By default, uf sandbox start creates ephemeral containers that are removed on stop. For long-running development sessions, create a persistent workspace that survives stop/start cycles.
sandbox create
Provision a persistent workspace backed by named Podman volumes. The workspace retains the container’s filesystem state across restarts.
uf sandbox create # auto-named
uf sandbox create --name my-project-sandbox # custom name
uf sandbox create --demo-ports 3000,8080 # expose demo ports| Flag | Description |
|---|---|
--name <string> | Workspace name override (default uf-sandbox-<project-name>) |
--backend <string> | Backend: auto, podman, or che (default auto) |
--demo-ports <ints> | Additional ports to expose for demos (comma-separated) |
--image <string> | Container image (Podman only) |
--memory <string> | Memory limit (default 8g) |
--cpus <string> | CPU limit (default 4) |
--detach | Start without attaching the TUI |
--ide <string> | IDE for DevPod to open after creation: none (default), vscode, openvscode, fleet, jupyternotebook, cursor |
--provider <string> | DevPod provider name (default podman). References a registered DevPod provider — does not require the binary in PATH. Registered automatically by uf setup |
--uidmap | Use explicit UID/GID mapping |
sandbox destroy
Permanently delete a persistent workspace and all associated state (named volumes, CDE workspace). Ephemeral containers are cleaned up directly without requiring workspace resolution.
uf sandbox destroy # interactive confirmation
uf sandbox destroy --yes # skip confirmation
uf sandbox destroy --force # destroy even if running| Flag | Description |
|---|---|
--yes | Skip the confirmation prompt |
--force | Force destroy even if the workspace is running |
Workspace Detection
Once a persistent workspace exists, start, stop, and extract automatically detect and use it. You don’t need to pass any extra flags — the commands recognize that a workspace exists for the current project and operate on it.
Server Auto-Start
DevPod workspaces auto-start the OpenCode server via a postStartCommand in the devcontainer configuration. After both create and start, the sandbox runs a health check with exponential backoff (500ms to 5s intervals, 60s timeout) to confirm the server is ready before attaching.
If the postStartCommand did not run (e.g., the devcontainer was created outside the normal flow), the SSH fallback starts the server manually when attaching.
Troubleshooting: If
devpod upfails during workspace creation or start, runuf doctorto check DevPod provider configuration and Podman runtime health. If the provider is misconfigured or missing, runuf setupto reinstall Podman (step 14), DevPod (step 15), and the DevPod Podman provider (step 16).
Bidirectional Git Sync
Persistent workspaces maintain bidirectional git synchronization between the host and container. When the workspace starts, it syncs the host’s git state into the container. When you extract changes, they flow back via git format-patch / git am.
Demo Endpoint Port Forwarding
Ports specified with --demo-ports during create are forwarded from the container to the host. Use this for web applications or API servers that need to be accessed from outside the container. uf sandbox status displays the active demo endpoints.
CDE Backend
DevPod with Podman is the primary and recommended CDE backend. The sandbox defaults to DevPod for all workspace operations.
Backend Selection
uf sandbox create --backend podman # force Podman/DevPod backend (default)Backend resolution follows a priority chain:
| Priority | Source | Example |
|---|---|---|
| 1 | CLI flag | --backend podman |
| 2 | Environment variable | UF_SANDBOX_BACKEND=podman |
| 3 | Config file | sandbox.backend: podman in .uf/config.yaml |
| 4 | Auto-detect | Podman (default) |
Eclipse Che / Dev Spaces (Legacy — Deprecated)
Deprecated: The Eclipse Che / Dev Spaces backend is deprecated. Using
--backend chereturns a migration error directing you to use DevPod instead. The Che backend had API coverage and mocked tests but was never validated end-to-end with a production Eclipse Che instance.
uf sandbox create --backend che # returns migration errorIf you encounter a migration error when using --backend che, switch to the default DevPod backend by removing the --backend che flag (or setting --backend podman). Run uf setup to install DevPod and configure the Podman provider if not already set up.
IDE Integration
The --ide flag controls which IDE DevPod opens after workspace creation or start. The value is resolved through a priority chain:
| Priority | Source | Example |
|---|---|---|
| 1 | CLI flag | --ide vscode |
| 2 | Environment variable | UF_SANDBOX_IDE=cursor |
| 3 | Config file | sandbox.ide: vscode in .uf/config.yaml |
| 4 | Default | none (no IDE opened) |
Valid values: none, vscode, openvscode, fleet, jupyternotebook, cursor.
When set to a value other than none, DevPod opens the specified IDE and connects it to the workspace after the container is ready and the health check passes.
Security Model
The sandbox provides several isolation layers:
| Property | Description |
|---|---|
| Rootless Podman | Container runs without root privileges on the host |
| Read-only mounts | Isolated mode mounts the project read-only (default) |
| No push credentials | Git push credentials are never forwarded to the container |
| Resource limits | Memory (default 8g) and CPU (default 4) limits prevent runaway processes |
| SELinux | Auto-detects SELinux and applies :Z volume labels on enforcing systems |
| Non-root user | Container runs as UID 1000 (non-root) inside the namespace |
API Key Forwarding
The sandbox forwards LLM API keys to the container so the agent can make API calls:
ANTHROPIC_API_KEY— Anthropic direct API accessOPENAI_API_KEY— OpenAI API accessGEMINI_API_KEY— Google Gemini API accessOPENROUTER_API_KEY— OpenRouter API access
Google Cloud / Vertex AI Integration
For Vertex AI users, the sandbox supports two credential paths:
- Service account key: If
GOOGLE_APPLICATION_CREDENTIALSpoints to a key file, the file is mounted into the container - Application Default Credentials (ADC): The
~/.config/gcloud/directory is mounted forgcloud auth application-defaulttoken flow
When a cloud provider is detected, the sandbox auto-starts the LLM gateway (uf gateway) to proxy API requests. The container receives ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN environment variables pointing to the gateway instead of direct credential mounts.
GPU-less Embedding Support
For developers without a local GPU who need embedding model support (e.g., for Dewey semantic search), the uf ollama-proxy command starts a proxy to a remote Ollama instance via the gateway. This allows sandbox sessions to access embedding models without requiring local GPU hardware. See the
CLI Reference for uf ollama-proxy subcommands and configuration.
See Also
- Development Environment Setup – Step-by-step guide to setting up a DevPod sandbox
- Gateway – LLM reverse proxy auto-started by the sandbox for cloud provider credential isolation
- Configuration –
sandbox.ide,sandbox.runtime, and other sandbox config fields - Quick Start – Install and verify the toolchain
- Developer Guide – Daily workflow with the
ufCLI - Common Workflows – End-to-end feature and review flows