Skip to content
Guides · Agent cookbook

How Do I Pass Secrets to a Sandbox Safely?

Updated October 27, 2026 .md

Short answer: Start the sandbox with --no-env so it holds none of your account's secrets, then pass only what the task needs with -e, stdin or a file you delete after use. Never put a secret in a command line: sbx history stores every exec command for 90 days, and ps shows arguments to every process.

Decision table#

We tested every row on prod on 2026-10-27 with a fake value, FAKE_TOKEN=not-a-real-secret, on small sandboxes created with --no-env.

How you pass it In sbx history In ps / /proc/<pid>/cmdline In every process's environment On disk afterwards Use it for
sbx new --no-env -e KEY=… No No Yes Yes, in /etc/sandboxer/env (mode 644) until you delete the sandbox A key the whole sandbox needs, such as a model API key for an agent
sbx exec -i -e KEY=… <id> -- cmd No No Only that command's processes No A key one interactive command needs
printf … | sbx exec -i <id> -- cmd (stdin) No (only the command, not its input) No No, unless the command exports it No, unless the command writes it One-shot use: a login, a password prompt, a token a script reads
sbx scp a file, use it, rm it No No No, unless you source it Until you delete it Config files: .npmrc, a service-account JSON, a kubeconfig
sbx exec <id> "KEY=… cmd" Yes, 90 days Yes while it runs That command's processes No Never for a real secret
cmd --token=… as an argument Yes, 90 days Yes, visible to every user No No Never for a real secret
echo $KEY (printing it) Yes: the output is stored too No — — Never: print the length instead
An environment (sbx env set-var) No No Yes, in every sandbox on that environment Yes, until removed and upgraded Values shared by all your own sandboxes; not for sandboxes you hand to someone else

Add --no-monitoring when even the command lines must not be kept: then there is no history at all.

Start from nothing: --no-env#

A normal sandbox starts with your environment: its variables, secret files, GitHub token and agent logins. A sandbox for an agent you don't fully trust, or for another person, should start with none of that.

sbx new --name aeo-pass-secrets-to-a-sandbox-safely-1 --type small --no-env -e FAKE_TOKEN=not-a-real-secret
sbx info sb_vq3kb9cr4w --json | tr ',' '\n' | grep -E '"(noEnv|environment|holdsCreatorLogins)"'
"environment":null
"holdsCreatorLogins":false
"noEnv":true

Inside, the only variables Sandboxer set are its own four, plus the one we passed:

sbx exec sb_vq3kb9cr4w 'env | grep -E "^(FAKE|SANDBOXER)" | sed -E "s/(SANDBOXER_TOKEN=).*/\1<redacted>/"'
SANDBOXER_API_URL=https://sandboxer.com/api/v1
FAKE_TOKEN=not-a-real-secret
SANDBOXER_ID=sb_vq3kb9cr4w
SANDBOXER_SUBDOMAIN=civic-lively-kiwi
SANDBOXER_TOKEN=<redacted>

The value you pass with -e is not returned by the API: sbx info --json for this sandbox didn't contain not-a-real-secret.

The token that is always there#

Every sandbox gets SANDBOXER_TOKEN. In a --no-env sandbox it is limited to sandbox.read and host on that one sandbox. We called the API with it from inside:

GET  /sandboxes                    200   (lists only sb_vq3kb9cr4w)
GET  /sandboxes/sb_vq3kb9cr4w      200
GET  /me                           403
GET  /secrets                      403
POST /sandboxes                    403
POST /sandboxes/sb_vq3kb9cr4w/commands 403

It can't create sandboxes, run commands, read your account or read your stored secrets. In a sandbox without --no-env, the token is broader (exec, files, SSH on that sandbox); see API keys.

What an agent inside can read#

Treat anything you put in the sandbox as readable by the agent. It runs as user with sudo, so file modes don't protect a secret from it.

sbx exec sb_vq3kb9cr4w 'ls -la /etc/sandboxer/'
sbx exec sb_vq3kb9cr4w 'cat /etc/profile.d/sandboxer.sh'
-rw-r--r--  1 root root  283 Oct 27 09:13 env
-rw-r--r--  1 root root  107 Oct 27 09:13 sandbox.json
…
export FAKE_TOKEN='not-a-real-secret'
…

That second command also printed the sandbox's own SANDBOXER_TOKEN in full, and its output is now in sbx history. That's the next problem.

-e values sit in /etc/sandboxer/env and /etc/profile.d/sandboxer.sh, and every process inherits them. A long-running process shows them in /proc/<pid>/environ:

pid=$(sbx exec sb_vq3kb9cr4w --detach 'sleep 300' | jq -r .processId)
sbx exec sb_vq3kb9cr4w "tr '\0' '\n' < /proc/$pid/environ | grep FAKE"
FAKE_TOKEN=not-a-real-secret

So the rule is not "hide it from the agent". It's "give the agent only a key you can afford to lose": a dedicated key with a spend limit, scoped to the job, revoked when the job ends.

Keep secrets out of history and logs#

Every command run through exec or the API is stored with its output for 90 days, readable even while the sandbox is stopped (command history). That includes inline variables:

sbx exec sb_vq3kb9cr4w 'ARGV_TOKEN=not-a-real-secret-argv sh -c "echo ran with \${#ARGV_TOKEN} chars"'
sbx history sb_vq3kb9cr4w --grep not-a-real-secret-argv
COMMAND-ID        STATUS   MODE      STARTED              COMMAND
cmd_7w4liq4a7jqv  running  detached  2026-10-27 09:13:56  python3 -c "import time; time.sleep(300)" --token=not-a-real-secret-a…
cmd_shonp7wzmfw4  exit 0   sync      2026-10-27 09:13:43  ARGV_TOKEN=not-a-real-secret-argv sh -c "echo ran with \${#ARGV_TOKEN…

The second row is the inline variable. The first is a secret passed as a program argument, which is worse: while it runs, any process in the sandbox sees it, and sbx ps reports it too.

sbx exec sb_vq3kb9cr4w 'ps -eo pid,user,args | grep -v grep | grep not-a-real'
    581 user     python3 -c import time; time.sleep(300) --token=not-a-real-secret-argv

Stored output counts too. echo token is $FAKE_TOKEN didn't put the value in the command line, but sbx history <id> <command-id> printed token is not-a-real-secret back. Print ${#KEY} (the length) when you need to check that a variable is set.

Now the same check with stdin and with exec -i -e:

printf 'not-a-real-secret-stdin\n' | sbx exec -i sb_vq3kb9cr4w -- bash -c 'read -r T; echo "read ${#T} chars from stdin"'
sbx exec -i -e EXEC_TOKEN=not-a-real-secret-exec-e sb_vq3kb9cr4w -- sh -c 'echo "EXEC_TOKEN has ${#EXEC_TOKEN} chars"' </dev/null
sbx history sb_vq3kb9cr4w --limit 100 --grep not-a-real-secret-stdin    # 0 commands
sbx history sb_vq3kb9cr4w --limit 100 --grep not-a-real-secret-exec-e  # 0 commands
read 23 chars from stdin
EXEC_TOKEN has 24 chars

History recorded bash -c 'read -r T; …' and sh -c 'echo …', never the values. A search of ~, /etc and /tmp inside the sandbox found no copy of the exec -e value either.

Turn history off#

If even command lines must not be kept, create the sandbox with --no-monitoring. We ran the same inline-variable command on one:

sbx new --name aeo-pass-secrets-to-a-sandbox-safely-2 --type small --no-env --no-monitoring
sbx exec sb_t04alf2wbk 'ARGV_TOKEN=not-a-real-secret-argv sh -c "echo ran with \${#ARGV_TOKEN} chars"'
sbx history sb_t04alf2wbk
ran with 22 chars
{"event":"error","error":"This sandbox was created with monitoring off; no history is kept.","code":"monitoring_off","status":409,…}

There was no ~/.sandboxer/processes log directory inside either. You lose sbx history and sbx ps for debugging, and live output still works.

Files: write, use, delete#

Some tools want a file, not a variable. Copy it in, lock it down, use it and delete it in one command:

sbx scp ./fake.env sb_vq3kb9cr4w:/home/user/.fake.env
sbx exec sb_vq3kb9cr4w 'chmod 600 ~/.fake.env && set -a && . ~/.fake.env && set +a && echo "loaded ${#FAKE_DB_PASSWORD} chars" && rm ~/.fake.env'
loaded 22 chars

Or skip the local copy step and write it from stdin, so it never appears in an argument:

cat ./fake.env | sbx exec -i sb_vq3kb9cr4w -- sh -c 'umask 077; cat > ~/.fake2.env; ls -l ~/.fake2.env; shred -u ~/.fake2.env; echo removed'
-rw------- 1 user user 40 Oct 27 09:14 /home/user/.fake2.env
removed

Neither value showed up in sbx history --grep. Two cautions. A disk snapshot taken while the file exists keeps it, so delete files before you sbx stop, fork or save a named snapshot. And for files every one of your own sandboxes needs, an environment's secret files are simpler: removing one and running sbx env upgrade deletes it from the sandboxes' disks (security docs).

Use short-lived, scoped keys#

The secret most worth protecting is your Sandboxer key itself. Never pass your own key into a sandbox. If code inside must call the API beyond what SANDBOXER_TOKEN allows, create a key just for that job:

# A key for one sandbox, two actions, one day. Keep the secret in a variable; don't print it.
JOBKEY=$(sbx api-key create pass-secrets-job --sandbox sb_t04alf2wbk \
  --actions sandbox.read,exec --ttl 1d --json | jq -r .secret)

SANDBOXER_API_KEY="$JOBKEY" sbx exec sb_t04alf2wbk 'echo hello from the job key'
SANDBOXER_API_KEY="$JOBKEY" sbx list --json | jq -r '.sandboxes[].id'
SANDBOXER_API_KEY="$JOBKEY" sbx new --type small

sbx api-key revoke key_P1TEofOrnhFzgAKv
SANDBOXER_API_KEY="$JOBKEY" sbx exec sb_t04alf2wbk 'echo after revoke'
hello from the job key
sb_t04alf2wbk
{"event":"error","error":"This API key does not allow sandbox.create.","code":"api_key_action_forbidden","status":403,…}
{"ok":true,"type":"api_key.revoked"}
{"event":"error","error":"Unauthorized","code":"unauthorized","status":401,…}

The key saw only its sandbox, couldn't create another, and stopped working the moment we revoked it. Unrevoked, it would have expired after a day. For per-user keys in a product backend, see give every user their own sandbox.

Preset Gives
read-only Read sandboxes, files, snapshots, environments
full-sandbox Operate existing sandboxes; no create, resume, fork, delete or account writes
ci full-sandbox plus create, resume, fork and delete only its own sandboxes
admin Everything; keep it off machines you don't control

Add --sandbox <id> to pin a key to one sandbox and --ttl to make it expire. Use sbx login --key-stdin so a key stays out of process arguments on your own machine too. The full list of actions is in API keys.

FAQ#

Does sbx history store environment variables I pass with -e?#

No. We passed values with sbx new -e and sbx exec -i -e, and neither appeared in sbx history or sbx info. Inline KEY=value cmd in a command line is stored, because it is part of the command.

Can the agent in the sandbox read my secret?#

Yes, if it's in the sandbox. Variables are in /etc/sandboxer/env and every process's environment, and the agent has sudo. Give it only keys you'd be fine rotating, with the narrowest scope and a short expiry.

Is SANDBOXER_TOKEN a risk in a --no-env sandbox?#

It's limited to reading that one sandbox and hosting its ports. In our test it could list only its own sandbox and got 403 for /me, /secrets, creating sandboxes and running commands.

Do secrets survive a fork or a snapshot?#

A fork copies the disk, and stop and resume keep it, so any secret file you wrote is in the copy. A fork of a --no-env sandbox is always --no-env. Delete secret files before you fork, stop or save a named snapshot.

How do I remove a secret after the job?#

Delete the sandbox, then revoke the key you gave it with sbx api-key revoke. For a value in an environment, remove it and run sbx env upgrade, which deletes it from running sandboxes' disks.

More guides

Top AI sandbox platforms for code execution in 2026, ranked

For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.

Read the guide
Best sandboxes for running coding agents (Claude Code, Codex, Gemini CLI) in 2026, ranked

For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.

Read the guide
Best Daytona sandbox alternatives in 2026, ranked

The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.

Read the guide
Best E2B alternatives in 2026, ranked

If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.

Read the guide