How to Customize the Environment in Microsandbox: Sandbox-Wide and Per-Command Variables

You can customize the environment in Microsandbox by setting sandbox-wide variables that apply to all commands, per-command variables for single invocations, or secret variables that never expose values inside the guest.

The Microsandbox SDK provides a layered approach to environment customization across Rust, Python, Node.js TypeScript, and Go. Whether you need global configuration values, temporary overrides for specific commands, or secure secrets that stay hidden from logs and guest inspection, the environment system gives you precise control over what reaches your sandboxed workloads.

Sandbox-Wide Environment Variables

Sandbox-wide variables are available to every command that runs inside the sandbox. These are set at build time and stored in the sandbox's spec.env list.

Setting Global Environment Variables

Use your SDK's builder API to define variables that persist across all executions:

Rust (sdk/rust/lib/sandbox/builder.rs)

let sb = Sandbox::builder("demo")
    .env("APP_ENV", "production")
    .env("LOG_LEVEL", "info")
    .create().await?;

Python (sdk/python/src/helpers.rs)

sb = await Sandbox.create(
    "demo",
    env={"APP_ENV": "production", "LOG_LEVEL": "info"}
)

Node.js TypeScript (sdk/node-ts/native/sandbox_builder.rs)

const sb = await microsandbox.Sandbox.builder("demo")
    .env("APP_ENV", "production")
    .env("LOG_LEVEL", "info")
    .create();

Go (sdk/go/native/src/lib.rs)

sb, _ := microsandbox.NewSandbox(
    "demo",
    microsandbox.WithEnv("APP_ENV", "production"),
    microsandbox.WithEnv("LOG_LEVEL", "info"),
)

These values merge with any defaults baked into the container image, with builder-provided values taking precedence over duplicates.

Per-Command Environment Variables

Override or extend the environment for a single exec or shell invocation without affecting the sandbox's global configuration.

Temporary Environment Overrides

Rust

let out = sb.exec(
    "sh",
    ["-c", "echo $APP_ENV"],
    |e| e.env("APP_ENV", "staging")  // Overrides global "production"
).await?;

Python

out = await sb.exec(
    "sh",
    ["-c", "echo $APP_ENV"],
    env={"APP_ENV": "staging"}
)

Node.js TypeScript

const out = await sb.exec(
    "sh",
    ["-c", "echo $APP_ENV"],
    { env: { APP_ENV: "staging" } }
);

Go

out, _ := sb.Exec(
    "sh",
    []string{"-c", "echo $APP_ENV"},
    microsandbox.ExecEnv(map[string]string{"APP_ENV": "staging"}),
)

Per-command variables are ideal for testing different configurations, injecting request-specific metadata, or temporarily elevating log verbosity.

Secret Environment Variables

For sensitive values that must never appear in logs, process listings, or the guest environment, use the secret API (SandboxBuilder::secret_env in Rust, sandbox.secret_env in Python, sandbox.secretEnv in Node.js TypeScript, builder.SecretEnv in Go).

Secrets are sent only to explicitly allowed hosts. The system auto-generates a placeholder ($MSB_<ENV_VAR>) for use inside the sandbox—the real value remains protected outside the allowed communication channel.

Using Secret Variables

Rust

let sb = Sandbox::builder("demo")
    .secret_env("OPENAI_API_KEY", api_key, "api.openai.com")
    .create().await?;

Python

sb = await Sandbox.create(
    "demo",
    secrets=[{
        "env": "OPENAI_API_KEY",
        "value": os.getenv("OPENAI_API_KEY"),
        "allowed_host": "api.openai.com"
    }]
)

Node.js TypeScript

const sb = await microsandbox.Sandbox.builder("demo")
    .secretEnv("OPENAI_API_KEY", process.env.OPENAI_API_KEY!, "api.openai.com")
    .create();

Go

sb, _ := microsandbox.NewSandbox(
    "demo",
    microsandbox.WithSecretEnv("OPENAI_API_KEY", apiKey, "api.openai.com"),
)

The allowed_host parameter restricts where the secret can be sent, providing defense in depth for API keys, database credentials, and other sensitive tokens.

Reserved Keys and Validation

The Microsandbox protocol defines reserved environment keys in crates/protocol/lib/lib.rs (lines 123-411). Keys prefixed with MSB_ are reserved for internal use—examples include MSB_BLOCK_ROOT and MSB_NET.

Attempts to set user-defined variables with the MSB_ prefix are rejected with a clear error. This validation occurs in sdk/rust/lib/sandbox/builder.rs at line 922, ensuring protocol integrity and preventing accidental conflicts with runtime-critical values.

How Environment Merging Works

When a sandbox is built, sdk/rust/lib/sandbox/config.rs (functions merge_env and merge_init_env, lines 492-506) combines three sources in order:

  1. Image defaults (image.env) — Loaded from the container image
  2. Builder-wide env (SandboxBuilder::env) — Merged after image defaults, overriding duplicates
  3. Init-time env (InitOptionsBuilder) — Applied only to the init process (see sandbox/init.rs)

Both sandbox-wide and per-command variables ultimately become part of the protocol environment block that the runtime injects into the guest process.

Summary

  • Sandbox-wide variables apply to all commands via builder methods (SandboxBuilder::env, Sandbox.env, sandbox.env, builder.Env)
  • Per-command variables override for single invocations via execution options (ExecOptionsBuilder::env, Sandbox.exec(..., env=...), exec.env, ExecOptions.Env)
  • Secret variables protect sensitive values from exposure using host-restricted placeholders (secret_env, secretEnv, SecretEnv)
  • Reserved keys with MSB_ prefix are rejected by validation logic in builder.rs
  • Merge order is: image defaults → builder env → init-time env, with later sources overriding earlier ones

Frequently Asked Questions

Can I use both sandbox-wide and per-command environment variables together?

Yes. Per-command variables layer on top of sandbox-wide variables. When you specify env in an exec call, those values override any matching keys from the global environment for that single invocation. Non-conflicting keys from both sources remain available.

What happens if I try to set a variable with the MSB_ prefix?

The SDK rejects the request with a validation error. In sdk/rust/lib/sandbox/builder.rs at line 922, the code checks for this prefix and prevents user-defined variables from conflicting with reserved protocol keys like MSB_BLOCK_ROOT and MSB_NET. Choose variable names without the MSB_ prefix.

How are secret variables different from regular environment variables?

Secret variables never expose their real values inside the sandbox or in any logs. Instead, the system generates a placeholder ($MSB_OPENAI_API_KEY for a secret named OPENAI_API_KEY). The actual value is transmitted only to the explicitly allowed host you specified. This protects API keys and credentials from guest inspection and process dumps.

Which SDK methods handle environment configuration in Microsandbox?

The core methods are SandboxBuilder::env and SandboxBuilder::secret_env in Rust, available with equivalent names across all SDKs. The merge logic lives in sdk/rust/lib/sandbox/config.rs, validation in sdk/rust/lib/sandbox/builder.rs, and protocol constants in crates/protocol/lib/lib.rs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →