Environment variables
Guest envVars per call and per context, setEnvVars layering, per-language access, the SANDBOX_IDLE_TTL_MS / INTERPRETER_IDLE_TTL_MS settings, and disabling the File API with SANDBOX_FILE_API.
Environment variables work at two layers: guest-visible envVars, passed into executing code, and Worker vars, which configure a Worker itself (Wrangler’s own environment variables) — and on this side, there are two separate idle-TTL settings, one per Worker.
Guest environment variables
envVars works the same way in both modes — pass it as a call option. In stateless mode:
import { runCode } from "@sandbox-workers/core";
await runCode(env.PYTHON, code, { envVars: { X: "12" } });
In stateful mode:
await sandbox.interpreter.runCode(code, { envVars: { X: "12" } });
Keys must match /^[A-Za-z_][A-Za-z0-9_]*$/; values must be strings. A null or undefined value is skipped rather than passed through. Only the key/value pairs supplied this way — merged across the layers below in stateful mode — are visible to guest code; nothing from either Worker’s own host environment leaks through.
Each language reads them differently:
| Language | Access |
|---|---|
| JavaScript | process.env.NAME |
| Python | os.environ["NAME"] |
| Perl | $ENV{NAME} |
| Ruby | ENV["NAME"] |
Sandbox-level: setEnvVars (stateful mode only)
sandbox.setEnvVars() sets variables for every execution in the sandbox, across every context and every binding, until changed again:
await sandbox.setEnvVars({ NAME: "value", OLD: undefined }); // undefined unsets a key
Passing undefined for a key removes it rather than setting the literal string "undefined".
Context-level: envVars on createCodeContext (stateful mode only)
A code context can carry its own envVars, set once when the context is created:
const ctx = await sandbox.interpreter.createCodeContext({ binding: "PYTHON", envVars: { MODE: "test" } });
Layering order
When the same key is set at more than one layer, a later source overrides an earlier one, in this order:
- Sandbox-level (
setEnvVars) - Context-level (
createCodeContext({ envVars })) - Call-level (
runCode(code, { envVars }))
An explicit null/undefined at any layer unsets that key for the merged result rather than passing the literal string through to the guest. This full merge happens in the Sandbox Durable Object, which sends the flat, already-merged result to the runtime Worker.
Two idle-TTL settings, on two Workers (stateful mode only)
Both settings below apply only to stateful mode: a stateless call has no sandbox and no code context to expire. Because a sandbox and a code context’s interpreter live in different Durable Objects — possibly in Workers you deployed independently — there are two separate settings:
SANDBOX_IDLE_TTL_MS — your own Worker
Configures how long a sandbox — every code context’s registry entry plus /workspace — can sit idle before your Sandbox Durable Object deletes it:
// your wrangler.jsonc
{
"vars": { "SANDBOX_IDLE_TTL_MS": "86400000" }, // 24 hours (the default); "0" disables expiry
}
INTERPRETER_IDLE_TTL_MS — each runtime Worker
Configures how long a runtime Worker’s Interpreter Durable Object keeps a given sandbox’s contexts’ memory snapshots before wiping them:
// this runtime Worker's wrangler.jsonc
{
"vars": { "INTERPRETER_IDLE_TTL_MS": "3600000" }, // 1 hour; "0" disables expiry
}
Both default to 24 hours (Wrangler vars are always strings; unset or invalid values fall back to the default), and both treat "0" as “never expire.”
Set INTERPRETER_IDLE_TTL_MS to at least SANDBOX_IDLE_TTL_MS. If a runtime Worker’s interpreter expires first, its memory snapshots are gone while the sandbox’s registry still lists the context as live — the next runCode against it fails with ContextNotFoundError even though sandbox.getInfo() still shows the context. See Sandboxes for the full idle-expiry behavior on both sides.
Disabling the File API (stateful mode only)
SANDBOX_FILE_API — your own Worker
Turns the File API off for every sandbox your Sandbox Durable Object hosts:
// your wrangler.jsonc
{
"vars": { "SANDBOX_FILE_API": "disabled" }, // optional; unset or any other value keeps it enabled
}
When disabled:
- Every file method (
writeFile,readFile,mkdir,deleteFile,renameFile,moveFile,listFiles,exists) throwsNotSupportedError(code: "NOT_SUPPORTED", HTTP 403) instead of running. On the wire,POST /filesanswers 403. See Errors. /workspaceis never persisted: theSandboxDurable Object neither reads nor writes itsfilestable, and the workspace diff a runtime Worker returns afterrunCodeis discarded.- Guest code loses
/workspacetoo. EveryrunCodesends an empty workspace manifest withdisabled: true, and the interpreter refuses every read, write, mkdir, delete, rename, and directory listing under/workspacewithEACCES(a JavaScriptfs.*Errorwithcode: "EACCES", a PythonPermissionError, a Perlopenfailure with$!set toPermission denied, and a failedimport/requireof a module from/workspace).process.cwd()/os.getcwd()still return/workspace— the directory still exists as the cwd, just empty and inaccessible. Memory snapshots, globals, and code contexts are unaffected. sandbox.getInfo()reportsfileApi: false, andworkspacethen always reports{ files: 0, bytes: 0 }. See Lifecycle.- This guest-side lockout requires runtime Workers built from a version that understands the
disabledmanifest flag — an older runtime Worker ignores it. Nothing is stored either way (the sandbox still discards its writes), but guest code on an older runtime Worker wouldn’t seeEACCES.
Related resources
- Wrangler configuration — where
varsand other Worker settings live. - Execute code — passing
envVarstorunCode. - Code interpreter —
createCodeContext,setEnvVars, andrunCodesignatures. - Sandboxes — idle expiry behavior in full, including what happens when the timers mismatch.
- Files — the File API this setting disables, and its error handling.