---
description: Pass configuration, secrets, and runtime settings to Sandbox SDK containers using environment variables.
title: Environment variables
image: https://developers.cloudflare.com/og-docs.png
---

[Skip to content](#main-content)

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

# Environment variables

Last updated Aug 24, 2026|Copy as Markdown|[View as Markdown](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/configuration/environment-variables/index.md)|[Agent setup](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/agent-setup/)

Coming soon: Sandbox SDK 1.0

This page documents environment configuration on today's stable `@cloudflare/sandbox` package.

For `@cloudflare/sandbox@next`, refer to [Environment variables](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/1-0-preview/environment/) in the 1.0 preview section.

Pass configuration, secrets, and runtime settings to your sandboxes using environment variables.

## SDK configuration variables

These environment variables configure how the Sandbox SDK behaves. Set these as Worker `vars` in your `wrangler.jsonc` file. The SDK reads them from the Worker's environment bindings.

### SANDBOX\_TRANSPORT

| **Type**    | "http" \| "websocket" | "rpc" |
| ----------- | --------------------- | ----- |
| **Default** | "http"                |       |

Controls the transport protocol for SDK-to-container communication. RPC transport multiplexes all operations over a single persistent connection, avoiding [subrequest limits](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/workers/platform/limits/#subrequests) when performing many SDK operations per request.

```jsonc
{
	"vars": {
		"SANDBOX_TRANSPORT": "rpc"
	}
}
```

```toml
[vars]
SANDBOX_TRANSPORT = "rpc"
```

For a complete guide including valid transport modes, performance considerations, and migration instructions, refer to [Transport modes](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/configuration/transport/).

### COMMAND\_TIMEOUT\_MS

| **Type**    | number (milliseconds) |
| ----------- | --------------------- |
| **Default** | None (no timeout)     |

Sets a global default timeout for every `exec()` call. When set, any command that exceeds this duration raises an error on the caller side and closes the connection.

Per-command `timeout` on `exec()` and session-level `commandTimeoutMs` on [createSession()](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/api/sessions/#createsession) both override this value. For more details on timeout precedence, refer to [Execute commands - Timeouts](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/guides/execute-commands/#timeouts).

```jsonc
{
	"vars": {
		"COMMAND_TIMEOUT_MS": "30000"
	}
}
```

```toml
[vars]
COMMAND_TIMEOUT_MS = "30000"
```

Note

A timeout does not kill the underlying process. It only terminates the connection to the caller. The process continues running until the session is deleted or the sandbox is destroyed.

## Three ways to set environment variables

The Sandbox SDK provides three methods for setting environment variables, each suited for different use cases:

### 1\. Sandbox-level with setEnvVars()

Set environment variables globally for all commands in the sandbox:

```typescript
const sandbox = getSandbox(env.Sandbox, "my-sandbox");

// Set once, available for all subsequent commands
await sandbox.setEnvVars({
	DATABASE_URL: env.DATABASE_URL,
	API_KEY: env.API_KEY,
});

await sandbox.exec("python migrate.py"); // Has DATABASE_URL and API_KEY
await sandbox.exec("python seed.py"); // Has DATABASE_URL and API_KEY

// Unset variables by passing undefined
await sandbox.setEnvVars({
	API_KEY: "new-key", // Updates API_KEY
	OLD_SECRET: undefined, // Unsets OLD_SECRET
});
```

**Use when:** You need the same environment variables for multiple commands.

**Unsetting variables**: Pass `undefined` or `null` to unset environment variables:

```typescript
await sandbox.setEnvVars({
	API_KEY: 'new-key',     // Sets API_KEY
	OLD_SECRET: undefined,  // Unsets OLD_SECRET
	DEBUG_MODE: null        // Unsets DEBUG_MODE
});
```

### 2\. Per-command with exec() options

Pass environment variables for a specific command:

```typescript
await sandbox.exec("node app.js", {
	env: {
		NODE_ENV: "production",
		PORT: "3000",
	},
});

// Also works with startProcess()
await sandbox.startProcess("python server.py", {
	env: {
		DATABASE_URL: env.DATABASE_URL,
	},
});
```

**Use when:** You need different environment variables for different commands, or want to override sandbox-level variables.

Note

Per-command environment variables with `undefined` values are skipped (treated as "not configured"), unlike `setEnvVars()` where `undefined` explicitly unsets a variable.

### 3\. Session-level with createSession()

Create an isolated session with its own environment variables:

```typescript
const session = await sandbox.createSession({
	env: {
		DATABASE_URL: env.DATABASE_URL,
		SECRET_KEY: env.SECRET_KEY,
	},
});

// All commands in this session have these vars
await session.exec("python migrate.py");
await session.exec("python seed.py");
```

**Use when:** You need isolated execution contexts with different environment variables running concurrently.

## Unsetting environment variables

The Sandbox SDK supports unsetting environment variables by passing `undefined` or `null` values. This enables idiomatic JavaScript patterns for managing configuration:

```typescript
await sandbox.setEnvVars({
	// Set new values
	API_KEY: 'new-key',
	DATABASE_URL: env.DATABASE_URL,

	// Unset variables (removes them from the environment)
	OLD_API_KEY: undefined,
	TEMP_TOKEN: null
});
```

**Before this change**: Passing `undefined` values would throw a runtime error.

**After this change**: `undefined` and `null` values run `unset VARIABLE_NAME` in the shell.

### Use cases for unsetting

**Remove sensitive data after use:**

```typescript
// Use a temporary token
await sandbox.setEnvVars({ TEMP_TOKEN: 'abc123' });
await sandbox.exec('curl -H "Authorization: $TEMP_TOKEN" api.example.com');

// Clean up the token
await sandbox.setEnvVars({ TEMP_TOKEN: undefined });
```

**Conditional environment setup:**

```typescript
await sandbox.setEnvVars({
	API_KEY: env.API_KEY,
	DEBUG_MODE: env.NODE_ENV === 'development' ? 'true' : undefined,
	PROFILING: env.ENABLE_PROFILING ? 'true' : undefined
});
```

**Reset to system defaults:**

```typescript
// Unset to fall back to container's default NODE_ENV
await sandbox.setEnvVars({ NODE_ENV: undefined });
```

## Common patterns

### Pass Worker secrets to sandbox

Securely pass secrets from your Worker to the sandbox. First, set secrets using Wrangler:

```bash
wrangler secret put OPENAI_API_KEY
wrangler secret put DATABASE_URL
```

Then pass them to your sandbox:

```typescript
import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";

interface Env {
	Sandbox: DurableObjectNamespace<Sandbox>;
	OPENAI_API_KEY: string;
	DATABASE_URL: string;
}

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const sandbox = getSandbox(env.Sandbox, "user-sandbox");

		// Option 1: Set globally for all commands
		await sandbox.setEnvVars({
			OPENAI_API_KEY: env.OPENAI_API_KEY,
			DATABASE_URL: env.DATABASE_URL,
		});
		await sandbox.exec("python analyze.py");

		// Option 2: Pass per-command
		await sandbox.exec("python analyze.py", {
			env: {
				OPENAI_API_KEY: env.OPENAI_API_KEY,
			},
		});

		return Response.json({ success: true });
	},
};
```

### Combine default and specific variables

```typescript
const defaults = { NODE_ENV: "production", LOG_LEVEL: "info" };

await sandbox.exec("npm start", {
	env: { ...defaults, PORT: "3000", API_KEY: env.API_KEY },
});
```

### Multiple isolated sessions

Run different tasks with different environment variables concurrently:

```typescript
// Production database session
const prodSession = await sandbox.createSession({
	env: { DATABASE_URL: env.PROD_DATABASE_URL },
});

// Staging database session
const stagingSession = await sandbox.createSession({
	env: { DATABASE_URL: env.STAGING_DATABASE_URL },
});

// Run migrations on both concurrently
await Promise.all([
	prodSession.exec("python migrate.py"),
	stagingSession.exec("python migrate.py"),
]);
```

### Configure transport mode

Set `SANDBOX_TRANSPORT` in your Worker's `vars` to switch between HTTP, WebSocket, and RPC transport. For details on when and how to configure each transport, refer to [Transport modes](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/configuration/transport/).

### Bucket mounting credentials

When mounting S3-compatible object storage, the SDK uses **s3fs-fuse** under the hood, which requires AWS-style credentials. For R2, generate API tokens from the Cloudflare dashboard and provide them using AWS environment variable names:

**Get R2 API tokens:**

1. Go to [**R2** \> **Overview** ↗](https://dash.cloudflare.com/?to=/:account/r2) in the Cloudflare dashboard
2. Select **Manage R2 API Tokens**
3. Create a token with **Object Read & Write** permissions
4. Copy the **Access Key ID** and **Secret Access Key**

**Set credentials as Worker secrets:**

```bash
wrangler secret put AWS_ACCESS_KEY_ID
# Paste your R2 Access Key ID

wrangler secret put AWS_SECRET_ACCESS_KEY
# Paste your R2 Secret Access Key
```

**Mount buckets with automatic credential detection:**

```typescript
import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";

interface Env {
	Sandbox: DurableObjectNamespace<Sandbox>;
	AWS_ACCESS_KEY_ID: string;
	AWS_SECRET_ACCESS_KEY: string;
}

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const sandbox = getSandbox(env.Sandbox, "data-processor");

		// Credentials automatically detected from environment
		await sandbox.mountBucket("my-r2-bucket", "/data", {
			endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
		});

		// Access mounted bucket using standard file operations
		await sandbox.exec("python", { args: ["process.py", "/data/input.csv"] });

		return Response.json({ success: true });
	},
};
```

The SDK automatically detects `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` from your Worker's environment when you call `mountBucket()` without explicit credentials.

**Pass credentials explicitly** (if using custom secret names):

```typescript
await sandbox.mountBucket("my-r2-bucket", "/data", {
	endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
	credentials: {
		accessKeyId: env.R2_ACCESS_KEY_ID,
		secretAccessKey: env.R2_SECRET_ACCESS_KEY,
	},
});
```

AWS nomenclature for R2

The SDK uses AWS-style credential names (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) because bucket mounting is powered by **s3fs-fuse**, which expects S3-compatible credentials. R2's API tokens work with this format since R2 implements the S3 API.

See [Mount buckets guide](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/guides/mount-buckets/) for complete bucket mounting documentation.

## Environment variable precedence

When the same variable is set at multiple levels, the most specific level takes precedence:

1. **Command-level** (highest) - Passed to `exec()` or `startProcess()` options
2. **Sandbox or session-level** \- Set with `setEnvVars()`
3. **Container default** \- Built into the Docker image with `ENV`
4. **System default** (lowest) - Operating system defaults

Example:

```typescript
// In Dockerfile: ENV NODE_ENV=development

// Sandbox-level
await sandbox.setEnvVars({ NODE_ENV: "staging" });

// Command-level overrides all
await sandbox.exec("node app.js", {
	env: { NODE_ENV: "production" }, // This wins
});
```

## Related resources

* [Transport modes](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/configuration/transport/) \- Configure HTTP, WebSocket, and RPC transport
* [Wrangler configuration](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/configuration/wrangler/) \- Setting Worker-level environment
* [Secrets](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/workers/configuration/secrets/) \- Managing sensitive data
* [Sessions API](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/api/sessions/) \- Session-level environment variables
* [Security model](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/concepts/security/) \- Understanding data isolation
* [Handle outbound traffic](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/sandbox/guides/outbound-traffic/) \- Keep credentials out of the sandbox entirely using outbound handlers

Was this helpful?

YesNo

## On this page

[![](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://docs-durable-objects-instance-replaced-errors.previews.developers.cloudflare.com/)

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/sandbox/configuration/environment-variables/#page","headline":"Environment variables · Cloudflare Sandbox SDK docs","description":"Pass configuration, secrets, and runtime settings to Sandbox SDK containers using environment variables.","url":"https://developers.cloudflare.com/sandbox/configuration/environment-variables/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-08-24","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
```
