Skip to main content

Sandbox Execution

Qwen Code supports sandboxed execution environments to isolate command execution and enhance security. This is particularly useful when running untrusted code or working in sensitive environments.

Supported Sandbox Runtimes

Qwen Code supports three sandbox backends:

Docker

Container-based isolation using Docker Engine.

Podman

Rootless container runtime, ideal for environments without Docker daemon privileges.

macOS Seatbelt (sandbox-exec)

Native macOS sandboxing without containers. Only available on macOS.

Configuration

Environment Variables

Enable Sandbox

# Auto-detect available runtime (seatbelt on macOS, manual on Linux)
export QWEN_SANDBOX=true

# Specify runtime explicitly
export QWEN_SANDBOX=docker
export QWEN_SANDBOX=podman
export QWEN_SANDBOX=sandbox-exec  # macOS only

Custom Sandbox Image

# Use a custom Docker/Podman image
export QWEN_SANDBOX_IMAGE=my-custom-image:latest

Additional Configuration

# Pass custom flags to Docker/Podman
export SANDBOX_FLAGS="--cpus=2 --memory=4g"

# Mount additional paths (comma-separated)
export SANDBOX_MOUNTS="/path/on/host:/path/in/container:ro,/another/path:/mnt/data:rw"

# Pass environment variables to sandbox
export SANDBOX_ENV="API_KEY=secret,DEBUG=true"

# Expose ports from sandbox to host
export SANDBOX_PORTS="3000,8080"

# Control UID/GID mapping (Linux only)
export SANDBOX_SET_UID_GID=true  # Default on Linux
export SANDBOX_SET_UID_GID=false # Use image default user

Command Line Options

# Enable sandbox with auto-detection
qwen --sandbox

# Specify runtime
qwen --sandbox=docker
qwen --sandbox=podman

# Custom image
qwen --sandbox-image=myorg/qwen-sandbox:v1

Settings File Configuration

In .qwen/settings.json:
{
  "tools": {
    "sandbox": "docker"
  }
}

Docker/Podman Sandbox Details

How It Works

When using Docker or Podman:
  1. Container Creation: Qwen Code spawns a container with:
    • Current working directory mounted
    • User settings directory (~/.qwen) mounted
    • Temporary directory mounted
    • Network access configured
  2. Environment Inheritance: The following are passed through:
    • API keys (GEMINI_API_KEY, OPENAI_API_KEY, etc.)
    • Terminal settings (TERM, COLORTERM)
    • Virtual environment paths (VIRTUAL_ENV)
    • Google Cloud credentials
  3. Lifecycle: Container is automatically removed on exit (--rm flag)

Volume Mounts

By default, these are mounted:
# Working directory
/path/to/project -> /path/to/project (inside container)

# User settings
~/.qwen -> /home/node/.qwen

# Temporary directory
/tmp -> /tmp

# Google Cloud config (if exists)
~/.config/gcloud -> ~/.config/gcloud:ro

UID/GID Handling

On Linux, Qwen Code automatically matches the host user’s UID/GID to prevent permission issues:
// From packages/cli/src/utils/sandbox.ts:68
async function shouldUseCurrentUserInSandbox(): Promise<boolean> {
  const envVar = process.env['SANDBOX_SET_UID_GID']?.toLowerCase().trim();
  
  if (envVar === '1' || envVar === 'true') return true;
  if (envVar === '0' || envVar === 'false') return false;
  
  // Default: true on Linux, false elsewhere
  if (os.platform() === 'linux') return true;
  return false;
}
This creates a user inside the container with matching UID/GID:
# Inside container setup
groupadd -f -g ${gid} qwen
useradd -o -u ${uid} -g ${gid} -d ${homeDir} -s /bin/bash qwen
su -p qwen -c '<command>'

macOS Seatbelt Sandbox

Built-in Profiles

Qwen Code includes six built-in seatbelt profiles:
export SEATBELT_PROFILE=permissive-open      # Default
export SEATBELT_PROFILE=permissive-closed
export SEATBELT_PROFILE=permissive-proxied
export SEATBELT_PROFILE=restrictive-open
export SEATBELT_PROFILE=restrictive-closed
export SEATBELT_PROFILE=restrictive-proxied

Profile Locations

  • Built-in: packages/cli/src/utils/sandbox-macos-{profile}.sb
  • Custom: .qwen/sandbox-macos-{profile}.sb (project-specific)

Custom Profile Example

Create .qwen/sandbox-macos-custom.sb:
(version 1)
(deny default)

; Allow reading from project directory
(allow file-read* (subpath (param "TARGET_DIR")))

; Allow writing to temp directory
(allow file-write* (subpath (param "TMP_DIR")))

; Allow network access
(allow network*)

; Allow process execution
(allow process-exec)
Then use it:
export SEATBELT_PROFILE=custom
qwen

Seatbelt Variables

Available in .sb files:
  • TARGET_DIR: Current working directory
  • TMP_DIR: System temporary directory
  • HOME_DIR: User home directory
  • CACHE_DIR: macOS cache directory
  • INCLUDE_DIR_0 through INCLUDE_DIR_4: Additional workspace directories

Proxy Support

For sandboxed environments with restricted network access:

Container-Based Proxy

# Proxy command runs in separate container with network access
export QWEN_SANDBOX_PROXY_COMMAND="mitmproxy --mode transparent"
export HTTPS_PROXY=http://localhost:8877

qwen --sandbox
The proxy container:
  • Runs on port 8877 by default
  • Connected to both host network and internal sandbox network
  • Automatically started/stopped with sandbox

macOS Seatbelt Proxy

export QWEN_SANDBOX_PROXY_COMMAND="mitmproxy --mode transparent"
export HTTPS_PROXY=http://localhost:8877
export SEATBELT_PROFILE=permissive-proxied

qwen

Custom Sandbox Images

Building Custom Image

Create .qwen/sandbox.Dockerfile:
FROM node:20-bookworm

# Install additional tools
RUN apt-get update && apt-get install -y \
    python3 \
    python3-pip \
    git \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Install Python packages
RUN pip3 install --no-cache-dir requests numpy pandas

# Set working directory
WORKDIR /workspace

# Copy entrypoint if needed
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

Build and Use

# Build the image
export BUILD_SANDBOX=1
qwen --sandbox

# Or build manually
docker build -t my-qwen-sandbox -f .qwen/sandbox.Dockerfile .

# Use custom image
export QWEN_SANDBOX_IMAGE=my-qwen-sandbox
qwen --sandbox

Startup Scripts

Container Bashrc

Create .qwen/sandbox.bashrc to customize the container environment:
# .qwen/sandbox.bashrc
# This file is sourced inside the sandbox container

# Set up Python virtual environment
if [ -d "venv" ]; then
    source venv/bin/activate
fi

# Add project bin to PATH
export PATH="$PWD/bin:$PATH"

# Set up Node.js version
if command -v nvm &> /dev/null; then
    nvm use
fi

# Custom aliases
alias ll='ls -la'
alias gs='git status'
This file is automatically sourced on container startup.

Troubleshooting

Permission Issues (Linux)

Problem: Files created in sandbox have wrong ownership. Solution: Enable UID/GID matching:
export SANDBOX_SET_UID_GID=true
qwen --sandbox

Image Not Found

Problem: Sandbox image 'qwen-code-sandbox' is missing Solution: Build the sandbox image:
# For linked development builds
export BUILD_SANDBOX=1
qwen --sandbox

# Or pull from registry (if available)
docker pull qwen-code-sandbox:latest

Network Access Issues

Problem: Container cannot access network. Solution: Check Docker/Podman network configuration:
# Test network access
qwen --sandbox -- curl -I https://google.com

# Check network mode
docker inspect <container-name> | grep NetworkMode

Virtual Environment Issues

Problem: Python venv not working in sandbox. Solution: The sandbox creates a separate venv directory:
# In .qwen/sandbox.bashrc
if [ ! -d ".qwen/sandbox.venv/bin" ]; then
    python3 -m venv .qwen/sandbox.venv
fi
source .qwen/sandbox.venv/bin/activate

Port Binding Issues

Problem: Cannot access exposed ports. Solution: Ensure ports are properly exposed:
# Expose ports
export SANDBOX_PORTS="3000,8080"

# Verify port mapping
docker ps
# Should show: 0.0.0.0:3000->3000/tcp

macOS Seatbelt Issues

Problem: Seatbelt denies file access. Solution: Use a more permissive profile or create custom profile:
# Use permissive profile
export SEATBELT_PROFILE=permissive-open

# Check sandbox logs
log stream --predicate 'process == "sandbox-exec"'

Security Considerations

Container Isolation

  • Containers run with --init flag for proper signal handling
  • Containers auto-removed on exit (--rm)
  • Host Docker socket NOT mounted by default
  • Network can be restricted using internal networks + proxy

Seatbelt Isolation

  • File system access restricted to defined paths
  • Network access can be blocked in restrictive profiles
  • Process execution can be limited

Best Practices

  1. Use restrictive profiles in production
  2. Mount only necessary paths with appropriate permissions (ro/rw)
  3. Review custom Dockerfiles before building
  4. Use proxy mode for network filtering
  5. Keep sandbox images updated with security patches

Integration Tests

For testing with sandbox enabled:
export QWEN_CODE_INTEGRATION_TEST=true
export QWEN_SANDBOX=docker

qwen --test
Integration test mode:
  • Uses unique container names with random suffixes
  • Supports checkpoint/resume functionality
  • Handles temporary file cleanup

Advanced Topics

Multiple Workspaces

Mount additional workspace directories:
export SANDBOX_MOUNTS="/workspace1:/ws1:ro,/workspace2:/ws2:rw"

Custom Entrypoint

Override container entrypoint:
export SANDBOX_FLAGS="--entrypoint /custom-entrypoint.sh"

Resource Limits

export SANDBOX_FLAGS="--cpus=2 --memory=4g --memory-swap=8g"

Debug Mode

Enable debugging:
export DEBUG=true
export DEBUG_PORT=9229

qwen --sandbox
# Debugger listening on 0.0.0.0:9229

Source Code References

  • Sandbox implementation: packages/cli/src/utils/sandbox.ts
  • Sandbox config: packages/cli/src/config/sandboxConfig.ts
  • Seatbelt profiles: packages/cli/src/utils/sandbox-macos-*.sb