CoreWeave
Runs code in a
CoreWeave Sandbox — a
container on CoreWeave's own GPU cloud, placed on a managed runner and
addressed through the cwsandbox SDK.
State persists between calls, though CoreWeave itself offers nothing that
holds a Python namespace: what it offers is a container and exec, a process
at a time. So the variant holds one itself. A single python -u -c process is
started with the sandbox and fed JSON lines on stdin, one request and one reply
each — the same arrangement the Modal variant uses, and for the same
reason:
sandbox.run_code("x = 40")
sandbox.run_code("x + 2").text # "42"
A session process that cannot be started, or that goes away mid-session, drops the sandbox back to a process per snippet — working, merely stateless — rather than failing the run. Which of the two you got is recorded on the sandbox, so a caller who depends on state can ask rather than assume:
sandbox.info.metadata["stateful"] # True when the session process is there
Pass stateful=False to turn the session process off and run every snippet on
its own.
- Requirements:
code-sandboxes[coreweave](installscwsandbox). - Parameters:
api_key,base_url,container_image,profile_names,runner_ids,python_executable,stateful.
How To Obtain CoreWeave Credentials​
- Create an access token in the CoreWeave console and export it:
It travels as a bearer token.export CWSANDBOX_API_KEY="..."
- Optionally point at another control plane:
export CWSANDBOX_BASE_URL="https://api.cwsandbox.com" # the default
Both can be passed to the sandbox instead of exported. The SDK authenticates from the environment and takes no token argument, so passing one here sets the variable for this process:
Sandbox.create(variant="coreweave", api_key="...", base_url="https://api.cwsandbox.com")
The product documentation is at docs.coreweave.com/products/sandboxes.
Usage​
from code_sandboxes import Sandbox
with Sandbox.create(variant="coreweave") as sandbox:
result = sandbox.run_code("import sys; sys.version_info[:2]")
print(result.text) # "(3, 11)"
The container is python:3.11 unless container_image= names another. An
image that carries what the code needs saves installing it on every run:
Sandbox.create(variant="coreweave", container_image="my-registry/analysis:2026-08")
GPUs​
CoreWeave is a GPU cloud, and a GPU is asked for the way it is everywhere else in this package:
Sandbox.create(variant="coreweave", gpu="H100")
CoreWeave places a sandbox on a runner of one kind and has no fallback list of
its own, so a comma-separated list is read as the first name in it rather than
as preferences to fall back along. The image has to carry what the code needs
to use the card — python:3.11 carries nothing of CUDA.
CPU and memory​
cpu and memory become Kubernetes quantities, sent as both the requests and
the limits of the container — cores as a whole number, memory rounded up to
whole gibibytes:
Sandbox.create(variant="coreweave", cpu=2.0, memory=4096)
Left out, CoreWeave's own defaults for the runner apply.
Network policy​
The policy of the configuration becomes CoreWeave's own network options:
# No egress and no ingress at all.
Sandbox.create(variant="coreweave", network_policy="none")
# Only these hosts, as egress rules — naming what may be reached denies the rest.
Sandbox.create(
variant="coreweave",
network_policy="allowlist",
allowed_hosts=["pypi.org", "files.pythonhosted.org"],
)
network_policy="allowlist" with no allowed_hosts is refused with the
reason: a sandbox allowed nothing is a sandbox with no network at all, which is
network_policy="none" and should say so.
Sandbox profiles and runners​
profile_names names CoreWeave's own policy objects — what a sandbox is
allowed to do — and runner_ids pins it to particular managed runners. Both
are left to CoreWeave when omitted, which is the usual case:
Sandbox.create(variant="coreweave", profile_names=["restricted"])
Names and tags​
CoreWeave keeps tags as a flat list of strings rather than as a map, so a pair
is written key=value. That is how the name and the tags of the configuration
survive the crossing and can be read back by list, next to
created-by=code-sandboxes, which is what tells the sandboxes this package
made from the rest of the organization's:
Sandbox.create(variant="coreweave", name="nightly-report", tags={"team": "ai"})
code-sandboxes list -v coreweave shows what is there; see the
management guide.
What Is Not Reported​
The session process answers with what the code printed, the value of its trailing expression, and the error it raised. There is no channel for anything else, so:
- The value of a trailing expression is reported — it is evaluated and its
reprcarried back in the reply, which is what makesresult.textwork above. - Rich display data — a figure, an HTML repr, a PNG — has no channel at all and
is not returned.
result.resultsonly ever holds the text value. - There is no interrupt:
sandbox.interrupt()answersFalse. A snippet that runs past its timeout has the SESSION stopped — not just the wait for its answer — because a snippet nobody is waiting for would otherwise go on changing the namespace. The next execution starts a new session, so nothing defined before the timeout is still there; the timeout message says so. - Contexts do not isolate: there is one session process and therefore one
namespace.
create_context()gives a context back, but two of them see each other's variables.
Variables cross as JSON (get_variable, set_variable, and everything built
on them such as sandbox.commands.run), so a value that cannot be encoded
comes back as its repr rather than as the object itself, and one that cannot
be sent is refused with a reason.
They need the session process, and say so when there is none — under
stateful=False, or after a session was stopped. Without it each snippet runs
in a process of its own, so a variable set by one would be gone before the next
could read it; refusing is the honest answer, and reporting a successful set
would not be.
Binary files go straight to CoreWeave's filesystem API rather than through a program that decodes them:
sandbox.files.write_bytes("/tmp/data.parquet", payload)
sandbox.files.read_bytes("/tmp/data.parquet")