# How to Pass Environment Variables to a Sandcastle Sandbox Using the `env` Option

> Learn how to pass environment variables to your Sandcastle sandbox using the env option. Easily inject key-value pairs to override repository and agent variables for custom sandbox behavior.

- Repository: [Matt Pocock/sandcastle](https://github.com/mattpocock/sandcastle)
- Tags: how-to-guide
- Published: 2026-05-24

---

**TLDR:** Supply an `env` object when calling a provider factory like `podman()` or `vercel()` to inject key-value pairs that override repository and agent environment variables inside the sandbox.

Sandcastle is an open-source sandbox execution framework developed by mattpocock. When running isolated workloads in Docker, Podman, or Vercel Firecracker environments, you often need to pass secrets or configuration via environment variables. The `env` option lets you inject these directly when configuring your sandbox provider, merging with variables from `.sandcastle/.env` while maintaining explicit precedence control.

## Understanding the `env` Option Interface

Each sandbox provider accepts an `env` option through its factory function. According to the source code in [`src/sandboxes/podman.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/sandboxes/podman.ts) (lines 75-77) and [`src/sandboxes/vercel.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/sandboxes/vercel.ts) (lines 111-112), the option is typed as `readonly env?: Record<string, string>`.

This interface is consistent across providers including Docker, Podman, Vercel, and the no-sandbox provider. When you invoke a factory, you pass an object containing your environment variables:

```ts
import { podman } from "sandcastle/sandboxes/podman";

const sandbox = podman({
  env: {
    API_TOKEN: "abc123",
    NODE_ENV: "production"
  }
});

```

## How Environment Variables Flow Through the System

Sandcastle merges environment variables from three distinct sources before starting the sandbox process.

### Provider Configuration and Forwarding

When you call `run()`, the `env` object flows through [`src/startSandbox.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/startSandbox.ts) (lines 34-36), which defines the `StartSandboxOptions` interface containing the `env` field. The system then passes this configuration to the provider's `create` method. For isolated sandbox creation, see lines 91-96 in [`src/startSandbox.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/startSandbox.ts) where `options.provider.create()` receives the merged environment.

### The Merge Process

Before the sandbox launches, Sandcastle combines variables using the `mergeProviderEnv` utility found in [`src/mergeProviderEnv.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/mergeProviderEnv.ts) (lines 8-12). The merge order follows this priority:

1. Repository-resolved environment (from `.sandcastle/.env` via [`EnvResolver.ts`](https://github.com/mattpocock/sandcastle/blob/main/EnvResolver.ts))
2. Agent-provided environment variables
3. **Sandbox-provider `env` option** (highest precedence)

Provider-specific environment variables override any duplicate keys from the other sources.

### Runtime Injection

The merged map becomes visible inside the container through runtime-specific mechanisms. In [`src/sandboxes/podman.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/sandboxes/podman.ts) (lines 45-49), the system constructs `envArgs` that translate to `-e KEY=VALUE` arguments for the Podman CLI. Similarly, the Vercel provider passes these variables to the Firecracker VM SDK.

## Provider-Specific Implementation Examples

### Passing Variables to a Podman Sandbox

The Podman provider accepts environment variables through its options interface and converts them to container runtime arguments. As implemented in [`src/sandboxes/podman.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/sandboxes/podman.ts) (lines 115-116), the `create` method receives the `env` object and processes it via the `envArgs` construction (lines 45-49):

```ts
import { podman } from "sandcastle/sandboxes/podman";
import { run } from "sandcastle";

await run({
  sandbox: podman({
    env: {
      DATABASE_URL: "postgres://user:pass@localhost:5432/db",
      LOG_LEVEL: "debug",
    },
  }),
  agent: myAgent,
});

```

### Configuring the Vercel Sandbox (Firecracker VM)

For serverless sandbox execution on Vercel's infrastructure, pass `env` to the `vercel()` factory. The implementation in [`src/sandboxes/vercel.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/sandboxes/vercel.ts) (lines 111-112 and 126-128) forwards these variables to the underlying Vercel SDK:

```ts
import { vercel } from "sandcastle/sandboxes/vercel";

await run({
  sandbox: vercel({
    env: {
      VERCEL_TOKEN: process.env.VERCEL_TOKEN!,
      CUSTOM_FLAG: "true",
    },
  }),
  agent: myAgent,
});

```

### Using the No-Sandbox Provider

Even when running directly on the host machine without containerization, you can control the environment. The no-sandbox provider accepts the same `env` interface, allowing you to extend or override `process.env`:

```ts
import { noSandbox } from "sandcastle/sandboxes/no-sandbox";

await run({
  sandbox: noSandbox({
    env: { PATH: "/custom/bin:" + process.env.PATH },
  }),
  agent: myAgent,
});

```

## Environment Variable Precedence and Resolution

Sandcastle resolves environment variables from multiple locations before merging them with your explicit `env` option. The [`EnvResolver.ts`](https://github.com/mattpocock/sandcastle/blob/main/EnvResolver.ts) file (lines 56-72) handles loading from `.sandcastle/.env` and falling back to `process.env`. 

When [`mergeProviderEnv.ts`](https://github.com/mattpocock/sandcastle/blob/main/mergeProviderEnv.ts) (lines 8-12) combines these sources, the provider-specific `env` always wins conflicts. This design ensures that explicit configuration in code overrides implicit repository settings or agent defaults.

## Summary

- Pass environment variables using the `env` option when calling provider factories like `podman()`, `vercel()`, or `noSandbox()`.
- The option is typed as `Record<string, string>` and defined in provider-specific files such as [`src/sandboxes/podman.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/sandboxes/podman.ts) (lines 75-77).
- Variables flow through [`src/startSandbox.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/startSandbox.ts) (lines 34-36) and merge with repository and agent environments via [`src/mergeProviderEnv.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/mergeProviderEnv.ts) (lines 8-12).
- Provider `env` values take highest precedence in the merge order.
- Podman and Docker providers convert the map to `-e` CLI arguments, while Vercel passes them to the Firecracker SDK.

## Frequently Asked Questions

### Can I pass sensitive secrets through the `env` option?

Yes. The `env` option is the recommended way to inject API tokens, database URLs, and other secrets into your Sandcastle sandbox. Since provider-specific environment variables take precedence over repository `.env` files, you can safely commit default configuration while injecting production secrets at runtime.

### What happens if I define the same variable in `.sandcastle/.env` and the `env` option?

The `env` option value wins. According to [`src/mergeProviderEnv.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/mergeProviderEnv.ts) (lines 8-12), Sandcastle merges sources with the provider `env` acting as the final override, ensuring explicit code configuration takes precedence over file-based configuration.

### Do environment variables work with the no-sandbox provider?

Yes. Even when using `noSandbox()` to execute directly on the host machine without containerization, the `env` option functions identically. It merges with the current process environment, allowing you to extend `PATH` or override specific variables for the agent execution.

### Which sandbox providers support the `env` option?

All Sandcastle providers support this interface: Docker (`docker()`), Podman (`podman()`), Vercel (`vercel()`), and the no-sandbox provider (`noSandbox()`). Each implements the `readonly env?: Record<string, string>` option as defined in their respective source files under `src/sandboxes/`.