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

# Configure a sandbox policy

> Set the constraints that bound what sandboxes on a cluster may request, and the defaults they inherit.

This page shows how to author the policy that governs sandboxes on a cluster: bounding compute, images, network access, and security posture, and setting the defaults every sandbox inherits. It is written for administrators who manage runners.

A policy is carried on the runner, so you author it by updating the runner. There is no separate policy resource and no policy endpoint. For how a sandbox resolves against a policy, see [Policies overview](/products/sandboxes/profiles/profiles).

<Note>
  CoreWeave sandboxes are in public preview. For access, contact your CoreWeave account team, [CoreWeave Support](https://cloud.coreweave.com/contact), or email [support@coreweave.com](mailto:support@coreweave.com).
</Note>

## Before you begin

You need the `sandbox_admin` role in your organization. Reading or writing a policy without it returns `PermissionDenied`.

To use the CLI examples, install and authenticate the [CoreWeave Intelligent CLI](https://github.com/coreweave/cwic). To use the REST examples, you need a CoreWeave API token. Examples use `$TOKEN` for that token and `prod-us-east-1` for the runner.

Every runner carries exactly one policy, and the policy is required. A policy with no base and no constraints is valid and declares a fully permissive posture, so an operator always states the posture explicitly rather than inheriting one by accident.

## Read the current policy

Start from what the runner has now, rather than from an empty document.

<Tabs>
  <Tab title="CLI">
    ```bash theme={"system"}
    cwic sandbox runner policy get prod-us-east-1
    ```

    Add `describe` for a detailed view, which names the not-configured state explicitly when a runner has no policy:

    ```bash theme={"system"}
    cwic sandbox runner policy describe prod-us-east-1
    ```

    With `-o json`, a runner that has never been configured returns `null` rather than `{}`, so automation can tell "never configured" apart from "configured and permissive".
  </Tab>

  <Tab title="curl">
    ```bash title="Read a runner and its policy" theme={"system"}
    curl https://api.coreweave.com/v1/sandbox/managedRunners/prod-us-east-1 \
      -H "Authorization: Bearer $TOKEN"
    ```
  </Tab>
</Tabs>

## Update a policy

A policy is a field on the runner, so you set it by updating the runner with an update mask of `policy`.

**A policy update always replaces the entire document.** Only `policy` is implemented as an update mask path, so you cannot patch one constraint group and leave the rest untouched. Read the current policy, change what you need, and send the whole document back.

<Tabs>
  <Tab title="CLI">
    Edit the policy in `$EDITOR`, seeded with the runner's current document:

    ```bash theme={"system"}
    cwic sandbox runner policy edit prod-us-east-1
    ```

    Apply a document from a file, or from stdin with `-`:

    ```bash theme={"system"}
    cwic sandbox runner policy edit prod-us-east-1 -f policy.yaml
    ```

    To start from a scaffold rather than the current document, print a template:

    ```bash theme={"system"}
    cwic sandbox runner policy edit prod-us-east-1 --print-template
    ```

    Check a document before sending it:

    ```bash theme={"system"}
    cwic sandbox runner policy validate -f policy.yaml
    ```
  </Tab>

  <Tab title="curl">
    ```bash title="Set the policy on a runner" theme={"system"}
    curl -X PATCH https://api.coreweave.com/v1/sandbox/managedRunners/prod-us-east-1 \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "managedRunner": {
          "policy": {
            "displayName": "Shared agent pool",
            "constraints": {
              "resources": { "maxCpu": "8", "maxMemory": "32Gi", "requireLimits": true },
              "lifecycle": { "defaultLifetimeSeconds": 3600 }
            }
          }
        },
        "updateMask": "policy"
      }'
    ```
  </Tab>
</Tabs>

Three rules apply to every policy write:

* **Lifetime is bounded by the platform, not the policy.** A policy supplies only `constraints.lifecycle.defaultLifetimeSeconds`, used when a create request omits a lifetime. The platform rejects lifetimes above its maximum, currently 30 days, at create time. Earlier policy versions carried a `maxLifetimeSeconds` cap; current `cwic` rejects documents that still contain it.
* **A configured policy cannot be cleared.** There is no delete operation, and the API rejects an update that sets the policy to null. To loosen a posture, replace the document with a more permissive one.
* **A policy document is capped at 256 KiB** once encoded.

<Warning>
  Because every write replaces the whole document, two administrators editing the same runner can overwrite each other. Read immediately before you write.
</Warning>

Policy changes apply to sandboxes created after the update. Running sandboxes keep the policy they resolved against at launch, and the gateway's cached copy can lag briefly, so a placement made moments after a write may still resolve against the previous policy.

<Note>
  Policy commands read and write the v1 API, while the other `cwic sandbox runner` and `cwic sandbox profile` commands still use v1beta2. Both are reached through the same `--api-url`.
</Note>

## Set a default policy for new runners

A runner that has never been used has no policy, and a sandbox that lands on it is rejected with `CWSANDBOX_POOL_POLICY_NOT_CONFIGURED`. To avoid configuring every runner by hand, a deployment can define a default that is written to a runner the first time v1 placement selects it.

The default lives in the gateway infrastructure configuration at `gateway.infraConfig.default_runner_policy`, and changing it requires a gateway restart. There is no built-in default. When the setting is absent or null, nothing is written and the sandbox is still rejected with `CWSANDBOX_POOL_POLICY_NOT_CONFIGURED`.

Two behaviors are worth knowing before you rely on it:

* **The default is a snapshot, not a link.** Once materialized onto a runner, the policy is an ordinary runner policy. Later changes to `default_runner_policy` do not reach runners that already have one. Update those with `UpdateManagedRunner`.
* **An invalid default degrades creation.** The gateway validates the configured policy at startup. If it does not validate, the gateway still starts, but sandbox creation fails with `CWSANDBOX_SERVERLESS_MISCONFIGURED`.

## Constrain compute

`resources` bounds what a container may request, and supplies the values used when a sandbox asks for nothing.

```json theme={"system"}
{
  "constraints": {
    "resources": {
      "minCpu": "500m",
      "maxCpu": "8",
      "defaultCpu": "2",
      "minMemory": "1Gi",
      "maxMemory": "32Gi",
      "defaultMemory": "4Gi",
      "requireLimits": true
    }
  }
}
```

Set `requireLimits` to `true` to reject sandboxes that do not declare both CPU and memory limits. Use `cpuCeiling` and `memoryCeiling` to cap the resolved total for a sandbox, which matters when the base layer contributes containers of its own.

## Constrain images

`image` restricts where sandbox images may come from. An empty group permits any image. Replace `[GITHUB-ORG]` with your GitHub Container Registry organization or user name.

```json theme={"system"}
{
  "constraints": {
    "image": {
      "allowedRegistries": ["ghcr.io/[GITHUB-ORG]"],
      "allowedImages": ["ghcr.io/[GITHUB-ORG]/agent-base:2024.11"]
    }
  }
}
```

## Constrain network access

Network constraints work as an envelope. A sandbox declares the access it needs, and the policy decides whether that request fits. The policy never adds access a sandbox did not ask for.

Two settings do separate jobs:

* `allowedEgress` is the entitlement envelope, checked when a sandbox is created. When it is non-empty, every rule the sandbox declares must fit inside it, and a rule outside it is rejected rather than narrowed. Empty means anything is declarable.
* `defaultEgress` is what an absent request means. It applies only when the sandbox declares no egress of its own, and any declaration displaces it entirely.

A rule names one destination. Use `cidr` with optional `except` carve-outs, `tenant` for a relationship to other sandboxes, or `any`. Optional `ports` narrow the rule further.

```json theme={"system"}
{
  "constraints": {
    "network": {
      "allowedEgress": [
        { "cidr": { "cidr": "0.0.0.0/0", "except": ["10.0.0.0/8", "169.254.169.254/32"] } },
        { "tenant": "TENANT_SCOPE_SAME_ORG" }
      ],
      "defaultEgress": [
        { "tenant": "TENANT_SCOPE_SAME_ORG" }
      ]
    }
  }
}
```

The example permits the public internet while carving out private space and the instance metadata address, and permits sandboxes to reach other sandboxes in the same organization. A sandbox that declares nothing gets same-organization access only.

Tenant scopes are `TENANT_SCOPE_SAME_USER` and `TENANT_SCOPE_SAME_ORG`. A third value, `TENANT_SCOPE_SANDBOX_NETWORK`, is not supported yet and an egress rule using it is rejected.

Ingress follows the same shape through `allowedIngress` and `defaultIngress`, for ports the sandbox exposes. Ingress rules accept `cidr`, `tenant`, or `any`, and never name-based sources, because inbound packets carry no name.

<Warning>
  Ingress is not enforced yet. A sandbox that declares ingress rules is rejected, so setting `allowedIngress` or `defaultIngress` has no effect today.
</Warning>

<Warning>
  DNS-name destinations are not enforced yet. A sandbox that declares one is rejected, so grant access by address range instead.
</Warning>

## Constrain security posture

`security` governs in-guest privilege and the isolation the sandbox runs under.

```json theme={"system"}
{
  "constraints": {
    "security": {
      "allowPrivileged": false,
      "allowedCapabilities": ["NET_BIND_SERVICE"],
      "allowedSeccompProfiles": ["RuntimeDefault"],
      "allowedRuntimeClasses": ["kata-qemu"],
      "defaultCpuRuntimeClass": "kata-qemu"
    }
  }
}
```

`allowedRuntimeClasses` fails closed. An empty or absent list permits no runtime class pin at all, so a sandbox can only select a class when you list one explicitly. Set `defaultCpuRuntimeClass` and `defaultGpuRuntimeClass` to choose what a sandbox gets when it pins nothing.

## Constrain instances, lifetime, metadata, and volumes

The remaining groups are small.

| Group       | Fields                                           | Effect                                                                                                                                                                                          |
| ----------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `instance`  | `allowedInstanceTypes`                           | Restricts which node instance types a sandbox may land on.                                                                                                                                      |
| `lifecycle` | `defaultLifetimeSeconds`                         | The lifetime a sandbox gets when it asks for nothing. The platform maximum, currently 30 days, bounds every sandbox; `maxLifetimeSeconds` was removed from the policy document and is rejected. |
| `metadata`  | `deniedAnnotationPrefixes`, `maxAnnotationCount` | Rejects annotation keys by prefix and caps how many a sandbox may set. `0` means no limit.                                                                                                      |
| `volumes`   | `allowedMedia`, `maxSize`                        | Restricts storage media and caps per-volume size. Omit `STORAGE_MEDIUM_MEMORY` to forbid RAM-backed volumes.                                                                                    |

```json theme={"system"}
{
  "constraints": {
    "lifecycle": { "defaultLifetimeSeconds": 3600 },
    "volumes": { "allowedMedia": ["STORAGE_MEDIUM_DISK"], "maxSize": "100Gi" }
  }
}
```

## The base layer

Constraints bound what a sandbox may ask for. The `base` layer supplies what every sandbox gets without asking: defaults and attachments such as the scheduler, node selectors, tolerations, service accounts, and image pull secrets. Its shape depends on the runner runtime, and on a Kubernetes runner it is a pod specification fragment.

## Next steps

* [Policies overview](/products/sandboxes/profiles/profiles): how a sandbox resolves against a policy, and what changed from profiles.
* [Deploy and manage a runner](/products/sandboxes/operations/managed-runners): the runner lifecycle and the rest of the runner configuration.
* [Control plane API overview](/products/sandboxes/reference/control-plane-api): authentication, field masks, and the endpoint contract.
