Secrets & Environment Variables
Pass credentials and configuration into sandboxes without hardcoding values
Sandboxes often need credentials — API keys, database URLs, tokens — to perform real work. There are several ways to get those values in safely.
The CUA_API_KEY environment variable
The Sandbox SDK reads your Cua API key from the CUA_API_KEY environment variable. Set it in your shell before running scripts:
export CUA_API_KEY="sk_cua-..."Or use a .env file with your preferred loader:
# .env
CUA_API_KEY=sk_cua-...from dotenv import load_dotenv
load_dotenv()
from cua import Sandbox, ImageYou can also pass the key explicitly if you manage it programmatically:
async with Sandbox.ephemeral(Image.linux(), api_key=my_key) as sb:
...Passing secrets into the sandbox at runtime
The recommended pattern is to read secrets from the host environment and inject them into the sandbox via sb.shell.run, not via Image.env().
import os
import asyncio
from cua import Sandbox, Image
async def main():
db_url = os.environ["DATABASE_URL"] # read from host
gh_token = os.environ["GITHUB_TOKEN"]
async with Sandbox.ephemeral(Image.linux()) as sb:
# Set inside the sandbox for this session only
await sb.shell.run(f"export DATABASE_URL='{db_url}'")
await sb.shell.run(f"export GITHUB_TOKEN='{gh_token}'")
await sb.shell.run("python /app/main.py")
asyncio.run(main())For multi-command sessions, write a wrapper script instead of exporting in each call:
script = f"""
export DATABASE_URL='{db_url}'
export GITHUB_TOKEN='{gh_token}'
python /app/main.py
"""
result = await sb.shell.run(script)Image.env() — bake-in non-secret config
Use .env() on the Image builder for configuration that isn't sensitive — feature flags, log levels, app settings:
from cua import Image
img = (
Image.linux()
.apt_install("python3")
.env(
LOG_LEVEL="info",
APP_ENV="production",
PORT="8080",
)
).env() values are stored in the Image spec and visible to anyone who can inspect it. Do not use .env() for API keys, passwords, or tokens.
Copying secret files into the sandbox
For credentials stored as files (SSH keys, service account JSON, certificates):
async with Sandbox.ephemeral(Image.linux()) as sb:
# Read the file on the host and write it in the sandbox
with open(os.path.expanduser("~/.ssh/id_rsa")) as f:
key_content = f.read()
await sb.shell.run(f"mkdir -p /root/.ssh && chmod 700 /root/.ssh")
await sb.shell.run(f"cat > /root/.ssh/id_rsa << 'EOF'\n{key_content}\nEOF")
await sb.shell.run("chmod 600 /root/.ssh/id_rsa")Alternatively, use Image.copy() for files that are safe to bake into the image:
img = Image.linux().copy("./app-config.json", "/app/config.json")Best practices
| Do | Don't |
|---|---|
| Read secrets from host environment variables | Hardcode secrets in Python source |
Inject secrets at runtime via sb.shell.run | Store secrets in Image.env() |
Use .env files + python-dotenv locally | Commit .env files to version control |
| Use your CI/CD provider's secret manager | Log or print secret values |
Was this page helpful?