Skip to main content
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.
CoreWeave Serverless sandboxes are in public preview.

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. 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.
Add describe for a detailed view, which names the not-configured state explicitly when a runner has no policy:
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”.

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.
Edit the policy in $EDITOR, seeded with the runner’s current document:
Apply a document from a file, or from stdin with -:
With -f, cwic reads the runner’s current revision as it submits, so a file edited against an earlier read can overwrite another administrator’s change. To pin the revision your file was based on, capture the policy and its etag from one read of the runner:
Edit policy.json, then submit it with the etag from that same read:
Take the etag from the read that produced the document, never from a later one. policy get exports the policy without its etag, and a policy describe run after the export can return a newer token, which passes the check and lets your file overwrite the change made in between. The curl tab and Replace the policy capture both from one response in the same way.Editor mode needs no flag, because it sends the etag it read to seed the editor.To start from a scaffold rather than the current document, print a template:
Check a document before sending it:
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.
A policy write is a read-modify-write, checked against the etag from the runner read your edit was based on. If another administrator has updated the policy since that read, the API rejects your write with FAILED_PRECONDITION and changes nothing. Read the runner again, reapply your change to the document that comes back, and resubmit with the new etag. Fetching a fresh etag and resending your unchanged document passes the check but discards the other administrator’s edit, because the write still replaces the whole document.
For lifetime examples and the differences between Serverless and CKS, see Run long-running sandboxes. 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.
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.

Set a default policy for new runners

CoreWeave configures a default policy for new runners in its hosted service. Other deployments can define their own default. The gateway writes that policy when a runner connects or when v1 placement first selects a runner without a policy. A runner with an existing policy keeps 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.
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. maxGpuCount caps the GPUs one sandbox may request. Leave it out to apply no cap. Set it to 0 to reject every GPU request, which is how you express a GPU-free pool. A request above the cap fails with CWSANDBOX_RESOURCE_CEILING_EXCEEDED.

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.

Constrain network access

Network constraints work as an envelope. When a sandbox declares egress, the policy accepts or rejects that declaration without widening it. When the sandbox declares no egress, the policy applies its egress defaults, unless the sandbox sets deny_egress. Two settings do separate jobs:
  • allowedEgress is the egress allowlist, 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 does not set deny_egress. Any declaration displaces it entirely.
A rule names one destination. Use cidr with optional except carve-outs, tenant for a relationship to other sandboxes, selector for workloads selected by label, httpsHostname for an HTTPS hostname, or any. Optional ports narrow the rule further. Hostname rules permit only TCP 443 and require a runner that supports them. They belong in allowedEgress and sandbox requests, not in defaultEgress.
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 uses allowedIngress and defaultIngress for ports with CUSTOM visibility. A non-empty allowlist constrains every declared ingress rule. When a sandbox exposes a CUSTOM port without declaring ingress rules, defaultIngress applies, unless the sandbox sets deny_ingress. Explicit ingress rules replace that default. Ingress rules accept cidr, tenant, or any; they do not accept name-based sources. Set denyHttpsHostnameRules to reject hostname-based HTTPS rules. This does not block DNS queries or HTTPS permitted by CIDR rules. Set dnsEgress to DNS_EGRESS_MODE_DENY to block outbound UDP/TCP port 53. If omitted, DNS access to the platform resolver is allowed. Platform DNS and storage access are granted separately from destination rules. See Network constraints for containment rules and DNS behavior.

Constrain security posture

security governs in-guest privilege and the isolation the sandbox runs under.
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.

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

Last modified on September 29, 2026