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): Run uf setup, which installs DevPod and registers a docker-type provider under the name podman (via DOCKER_PATH=podman). You do not need a standalone podman binary 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
FlagDescription
--mode <string>Mount mode: isolated (read-only, default) or direct (read-write)
--detachStart 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
--uidmapUse 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-parentMount 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 stop

sandbox 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 attach

sandbox 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
FlagDescription
--yesSkip 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 status

Mount 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 isolated

Your 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 direct

Your 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 --uidmap

This 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:

  1. Podman version too old: Upgrade to Podman >= 4.3
  2. macOS Podman machine: Restart the machine or use --uidmap
  3. Linux: Check that /etc/subuid and /etc/subgid have 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 init does not deploy a devcontainer template. Run uf sandbox init to 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
FlagDescription
--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)
--detachStart 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
--uidmapUse 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
FlagDescription
--yesSkip the confirmation prompt
--forceForce 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 up fails during workspace creation or start, run uf doctor to check DevPod provider configuration and Podman runtime health. If the provider is misconfigured or missing, run uf setup to 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:

PrioritySourceExample
1CLI flag--backend podman
2Environment variableUF_SANDBOX_BACKEND=podman
3Config filesandbox.backend: podman in .uf/config.yaml
4Auto-detectPodman (default)

Eclipse Che / Dev Spaces (Legacy — Deprecated)

Deprecated: The Eclipse Che / Dev Spaces backend is deprecated. Using --backend che returns 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 error

If 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:

PrioritySourceExample
1CLI flag--ide vscode
2Environment variableUF_SANDBOX_IDE=cursor
3Config filesandbox.ide: vscode in .uf/config.yaml
4Defaultnone (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:

PropertyDescription
Rootless PodmanContainer runs without root privileges on the host
Read-only mountsIsolated mode mounts the project read-only (default)
No push credentialsGit push credentials are never forwarded to the container
Resource limitsMemory (default 8g) and CPU (default 4) limits prevent runaway processes
SELinuxAuto-detects SELinux and applies :Z volume labels on enforcing systems
Non-root userContainer 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 access
  • OPENAI_API_KEY — OpenAI API access
  • GEMINI_API_KEY — Google Gemini API access
  • OPENROUTER_API_KEY — OpenRouter API access

Google Cloud / Vertex AI Integration

For Vertex AI users, the sandbox supports two credential paths:

  1. Service account key: If GOOGLE_APPLICATION_CREDENTIALS points to a key file, the file is mounted into the container
  2. Application Default Credentials (ADC): The ~/.config/gcloud/ directory is mounted for gcloud auth application-default token 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