> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.modbox.run/llms.txt.
> For full documentation content, see https://docs.modbox.run/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.modbox.run/_mcp/server.

# Dev Environments

Modbox can power ephemeral development environments — fully functional, isolated cloud workspaces that spin up in seconds and disappear when no longer needed.

## Use cases

* **PR preview environments** — Each pull request gets its own live environment
* **Pair programming sessions** — Share a live environment URL with a teammate
* **Workshops and tutorials** — Each participant gets their own pre-configured workspace
* **Feature testing** — QA engineers get isolated environments for testing branches

## Example: PR preview environments

Trigger a sandbox provisioning from your CI/CD pipeline when a PR is opened:

```yaml
# .github/workflows/preview.yml
name: PR Preview

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  provision-preview:
    runs-on: ubuntu-latest
    steps:
      - name: Provision sandbox
        run: |
          RESPONSE=$(curl -s -X POST https://api.modbox.run/sandboxes/provision \
            -H "Authorization: Bearer ${{ secrets.MODBOX_API_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{
              "task_id": "pr-${{ github.event.pull_request.number }}",
              "image_id": "${{ vars.DEV_IMAGE_ID }}",
              "ttl_seconds": 86400,
              "env_vars": {
                "GIT_BRANCH": "${{ github.head_ref }}",
                "PR_NUMBER": "${{ github.event.pull_request.number }}"
              }
            }')
          SANDBOX_ID=$(echo $RESPONSE | jq -r '.sandbox_id')
          echo "SANDBOX_ID=$SANDBOX_ID" >> $GITHUB_ENV

      - name: Wait for ready
        run: |
          curl -s -X POST https://api.modbox.run/sandboxes/wait \
            -H "Authorization: Bearer ${{ secrets.MODBOX_API_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{"task_id": "pr-${{ github.event.pull_request.number }}", "timeout": 120}'

      - name: Get sandbox URL
        run: |
          SANDBOX=$(curl -s https://api.modbox.run/sandboxes/detail/$SANDBOX_ID \
            -H "Authorization: Bearer ${{ secrets.MODBOX_API_TOKEN }}")
          URL=$(echo $SANDBOX | jq -r '.sandbox_url')
          echo "Preview ready: $URL" >> $GITHUB_STEP_SUMMARY

      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '**Preview environment ready!**\n\nYour sandbox is live and accessible. It will auto-destroy after 24 hours.'
            })
```

## Cleanup on PR close

```yaml
# Destroy sandbox when the PR is closed
on:
  pull_request:
    types: [closed]

jobs:
  destroy-preview:
    runs-on: ubuntu-latest
    steps:
      - name: Destroy sandbox
        run: |
          curl -s -X POST https://api.modbox.run/sandboxes/destroy \
            -H "Authorization: Bearer ${{ secrets.MODBOX_API_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{"task_id": "pr-${{ github.event.pull_request.number }}"}'
```

## Workshop environments

Provision environments for all participants at once:

```typescript
import { ModboxClient } from "modbox-sdk";

const modbox = new ModboxClient({ token: process.env.MODBOX_API_TOKEN });

const participants = ["alice", "bob", "carol", "dave"];

// Provision all environments in parallel
const sandboxes = await Promise.all(
  participants.map((name) =>
    modbox.provisionSandbox({
      taskId: `workshop-${name}`,
      imageId: process.env.WORKSHOP_IMAGE_ID,
      ttlSeconds: 14400, // 4 hours
      envVars: { PARTICIPANT: name },
    })
  )
);

// Wait for all to be ready
await Promise.all(
  participants.map((name) =>
    modbox.waitForSandbox({ taskId: `workshop-${name}`, timeout: 60 })
  )
);

// Print the access URLs
for (const name of participants) {
  const sandbox = await modbox.getSandbox(`workshop-${name}`);
  console.log(`${name}: ${sandbox.sandboxUrl}`);
}
// alice: https://workshop-alice.sandbox.modbox.run
// bob:   https://workshop-bob.sandbox.modbox.run
// ...
```