> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gravixlayer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Long-running and parallel Claude agents

> Run many Claude Code sessions across Gravix Layer runtimes — parallel agents, pause/resume, and scheduled jobs from your own orchestrator.

On Gravix Layer the ops model is simple: **one sandbox per job** (or one sandbox per long session), controlled egress, and the same runtime APIs you already use.

| Claude product idea                   | Gravix Layer approach                                                    |
| ------------------------------------- | ------------------------------------------------------------------------ |
| Many concurrent sessions (agent view) | Many `runtime_id`s; `runtime shell` into any one                         |
| Background a task                     | Long `run_cmd` timeout, or pause/resume later                            |
| Routines (schedule / webhook)         | Cron, GitHub Actions, or your API creates a runtime and runs `claude -p` |
| Dynamic multi-agent workflows         | Larger single runtime **or** fan-out across runtimes                     |

## Prerequisites

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
export GRAVIXLAYER_API_KEY="your-gravixlayer-api-key"
export ANTHROPIC_API_KEY="sk-ant-..."
export GRAVIXLAYER_CLAUDE_TEMPLATE="agent-claude"
export ANTHROPIC_PROVIDER_ID="..."      # from get-started
export CLAUDE_NETWORK_POLICY_ID="..."   # must allow api.anthropic.com
```

## Parallel agents (fan-out)

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import os
  from concurrent.futures import ThreadPoolExecutor, as_completed

  from gravixlayer import GravixLayer

  client = GravixLayer()
  TEMPLATE = os.environ["GRAVIXLAYER_CLAUDE_TEMPLATE"]
  PROVIDERS = [os.environ["ANTHROPIC_PROVIDER_ID"]]
  POLICIES = [os.environ["CLAUDE_NETWORK_POLICY_ID"]]
  ANTHROPIC_API_KEY = os.environ["ANTHROPIC_API_KEY"]

  TASKS = [
      "Write a minimal FastAPI /health endpoint under /workspace/a and stop.",
      "Write a minimal Express /health endpoint under /workspace/b and stop.",
      "Write a short Python hello script under /workspace/c/hello.py and stop.",
  ]

  def run_task(prompt: str) -> dict:
      rt = client.runtime.create(
          template=TEMPLATE,
          providers=PROVIDERS,
          network_policy_ids=POLICIES,
          env_vars={"ANTHROPIC_API_KEY": ANTHROPIC_API_KEY},
          timeout=1800,
          metadata={"task": prompt[:80]},
      )
      try:
          result = rt.run_cmd(
              command="claude",
              args=[
                  "--dangerously-skip-permissions",
                  "-p",
                  prompt,
                  "--output-format",
                  "text",
              ],
              timeout=1200,
          )
          return {
              "runtime_id": rt.runtime_id,
              "exit_code": result.exit_code,
              "success": result.success,
              "stdout": (result.stdout or "")[:2000],
          }
      finally:
          rt.kill()

  with ThreadPoolExecutor(max_workers=3) as pool:
      futures = [pool.submit(run_task, t) for t in TASKS]
      for fut in as_completed(futures):
          print(fut.result())
  ```

  ```bash CLI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  # Launch three runtimes (example: sequential; parallelize with background jobs if you prefer)
  for i in 1 2 3; do
    RT=$(gravixlayer runtime create \
      --template "$GRAVIXLAYER_CLAUDE_TEMPLATE" \
      --provider "$ANTHROPIC_PROVIDER_ID" \
      --network-policy "$CLAUDE_NETWORK_POLICY_ID" \
      --env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \
      --timeout 1800 \
      --wait \
      --output json | jq -r '.runtime_id')
    echo "started $RT"
    gravixlayer runtime exec "$RT" --timeout 1200 -- \
      claude --dangerously-skip-permissions \
      -p "Write a short note to /workspace/out-$i.txt and stop." \
      --output-format text
    gravixlayer runtime kill "$RT" -y
  done
  ```
</CodeGroup>

Reconnect to a live agent:

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
gravixlayer runtime shell "$RT"
# or continue headless:
gravixlayer runtime exec "$RT" -- \
  claude --dangerously-skip-permissions --resume "$SESSION_ID" -p "Continue"
```

## Pause and resume

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import json
import os
import time

from gravixlayer.types.runtime import Runtime

rt = Runtime.create(
    template=os.environ["GRAVIXLAYER_CLAUDE_TEMPLATE"],
    providers=[os.environ["ANTHROPIC_PROVIDER_ID"]],
    network_policy_ids=[os.environ["CLAUDE_NETWORK_POLICY_ID"]],
    env_vars={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
    timeout=0,  # no idle timeout (or set a multi-hour value)
)
print("runtime_id:", rt.runtime_id)

first = rt.run_cmd(
    command="claude",
    args=[
        "--dangerously-skip-permissions",
        "--output-format",
        "json",
        "-p",
        "Write /workspace/NOTES.md with a short inventory of /workspace, then stop.",
    ],
    timeout=3600,
)
session_id = json.loads(first.stdout).get("session_id")
print("session_id:", session_id)

rt.pause()
print("paused")

# Later (same or new process):
time.sleep(2)
rt = Runtime.connect(rt.runtime_id)
rt.resume()
print("resumed")

if session_id:
    rt.run_cmd(
        command="claude",
        args=[
            "--dangerously-skip-permissions",
            "--resume",
            session_id,
            "-p",
            "Append one more bullet to /workspace/NOTES.md.",
        ],
        timeout=900,
    )

print(rt.file.read("/workspace/NOTES.md").content)
rt.kill()
```

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
gravixlayer runtime pause "$RT"
gravixlayer runtime resume "$RT"
```

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# cURL
API="https://api.gravixlayer.ai/v1"
curl -sS -X POST "$API/agents/runtime/$RT/pause" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY"
curl -sS -X POST "$API/agents/runtime/$RT/resume" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY"
```

Pause keeps the filesystem (including Claude session files under `/workspace`).

## Scheduled routine (cron)

Complete worker script you can point cron at:

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
#!/usr/bin/env python3
"""morning_triage.py — create Claude sandbox, run a fixed prompt, print output, kill."""
import os
import sys

from gravixlayer import GravixLayer

TEMPLATE = os.environ["GRAVIXLAYER_CLAUDE_TEMPLATE"]
PROVIDER_ID = os.environ["ANTHROPIC_PROVIDER_ID"]
POLICY_ID = os.environ["CLAUDE_NETWORK_POLICY_ID"]
ANTHROPIC_API_KEY = os.environ["ANTHROPIC_API_KEY"]

PROMPT = os.environ.get(
    "CLAUDE_PROMPT",
    "Summarize what is under /workspace in five bullets. Write the summary to /workspace/briefing.md.",
)

def main() -> int:
    client = GravixLayer()
    runtime = client.runtime.create(
        template=TEMPLATE,
        providers=[PROVIDER_ID],
        network_policy_ids=[POLICY_ID],
        env_vars={"ANTHROPIC_API_KEY": ANTHROPIC_API_KEY},
        timeout=1800,
        metadata={"job": "morning_triage"},
    )
    try:
        result = runtime.run_cmd(
            command="claude",
            args=[
                "--dangerously-skip-permissions",
                "-p",
                PROMPT,
                "--output-format",
                "text",
            ],
            timeout=1200,
        )
        print(result.stdout)
        if not result.success:
            print(result.stderr, file=sys.stderr)
            return result.exit_code or 1
        briefing = runtime.file.read("/workspace/briefing.md")
        print("--- briefing.md ---")
        print(briefing.content)
        return 0
    finally:
        runtime.kill()

if __name__ == "__main__":
    raise SystemExit(main())
```

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# crontab example (weekdays 09:00)
# 0 9 * * 1-5 /usr/bin/env bash -lc 'cd /opt/jobs && source .env && python morning_triage.py'
```

## GitHub Action sketch

```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# .github/workflows/claude-pr-review.yml
name: Claude PR review
on:
  pull_request:
    types: [opened, synchronize]
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install gravixlayer
      - env:
          GRAVIXLAYER_API_KEY: ${{ secrets.GRAVIXLAYER_API_KEY }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GRAVIXLAYER_CLAUDE_TEMPLATE: ${{ vars.GRAVIXLAYER_CLAUDE_TEMPLATE }}
          ANTHROPIC_PROVIDER_ID: ${{ vars.ANTHROPIC_PROVIDER_ID }}
          CLAUDE_NETWORK_POLICY_ID: ${{ vars.CLAUDE_NETWORK_POLICY_ID }}
          GIT_CLONE_URL: ${{ github.event.pull_request.head.repo.clone_url }}
          GIT_BRANCH: ${{ github.head_ref }}
        run: python scripts/claude_pr_review.py
```

Implement `scripts/claude_pr_review.py` with `runtime.git.clone` + `claude -p "Review this PR..."` using the same patterns as [Repo & git](/guides/claude-code/repo-workflows).

## Operational checklist

* Cap concurrent runtimes (your pool size + [service quotas](/documentation/agentruntime/service-quotas))
* Tag with `metadata` (`task`, `pr_number`, `owner`)
* Always `kill` or set `timeout`
* Log `runtime_id`, Claude `session_id`, and `exit_code`

## Next

* [Custom templates & MCP](/guides/claude-code/custom-templates)
* [Create sandbox](/documentation/agentruntime/sandbox-management/create-sandbox)
