DaytonaDocs

Environment Variables & Secrets

Injecting configuration and credentials into sandboxes — and which mechanism to use.

Sandboxes usually need configuration: log levels, feature flags, endpoints — and credentials: API keys, tokens. Daytona provides two distinct mechanisms for getting values into a sandbox, and the difference between them is a security boundary, not a convenience. Plain env variables are visible to everything inside the sandbox; secrets are usable by code inside the sandbox but never readable by it.

Two mechanisms

envsecrets
What you passname → value mapnames of stored project secrets
What the guest seesreal valuesopaque placeholders (values substituted outside the sandbox on egress)
Best fornon-sensitive configAPI keys, tokens, credentials
Persistenceper-createstored once, reused across sandboxes

env — plain environment variables

The env parameter is a straightforward name → value map injected into the sandbox verbatim at create time. Every process in the sandbox can read the real values, exactly like environment variables anywhere else. Names must match [A-Za-z_][A-Za-z0-9_]* (invalid names fail the create with 400), and values are limited to 64 KiB each.

Use env for anything you would be comfortable printing in a log: configuration, endpoints, flags, non-sensitive identifiers.

Note: If the sandbox runs untrusted or AI-generated code, do not put credentials in env — that code can trivially read and exfiltrate them. Use secrets instead.

Python
from rlp import Daytona, CreateSandboxFromImageParams
 
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromImageParams(
    image="python:3.12-slim",
    env_vars={"LOG_LEVEL": "debug", "STAGE": "ci"},
))
 
print(sandbox.process.exec("echo $LOG_LEVEL").result)  # "debug"
TypeScript
import { Daytona } from '@rlp/sdk'
 
const daytona = new Daytona()
const sandbox = await daytona.create({
  image: 'python:3.12-slim',
  envVars: { LOG_LEVEL: 'debug', STAGE: 'ci' },
})
 
const r = await sandbox.process.executeCommand('echo $LOG_LEVEL')
console.log(r.result) // "debug"

secrets — protected credentials

Secrets solve the problem env cannot: giving sandboxed code the ability to use a credential without the ability to read it. A secret is a named value stored encrypted in your project. You attach secrets to a sandbox by listing their names in the secrets array at create time; each name must already exist in the project or the create fails with 400.

Inside the sandbox, each attached secret appears as an environment variable — but its value is an opaque placeholder, not the real credential. When code in the sandbox makes an outbound request carrying the placeholder (say, an Authorization header for an external API), the platform substitutes the real value on the way out — and only for hosts matching the secret's allowed_domains. The real value never enters the sandbox, so even fully untrusted code cannot exfiltrate it: dumping the environment, reading memory, or sending the placeholder to an attacker's server yields nothing usable.

Create secrets first — in the dashboard or via PUT /secrets/:name — then attach them by name:

Python
from rlp import Daytona, CreateSandboxFromImageParams
 
daytona = Daytona()
 
# Store once per project; scope substitution to the real API's domain.
daytona.secret.set(
    "OPENAI_API_KEY",
    "sk-...",
    allowed_domains=["api.openai.com"],
)
 
sandbox = daytona.create(CreateSandboxFromImageParams(
    image="python:3.12-slim",
    secrets=["OPENAI_API_KEY"],
))
 
# Inside the sandbox, $OPENAI_API_KEY is a placeholder — but requests to
# api.openai.com carry the real key.
sandbox.process.exec(
    'curl -s https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY"'
)
TypeScript
import { Daytona } from '@rlp/sdk'
 
const daytona = new Daytona()
 
await daytona.secret.set('OPENAI_API_KEY', 'sk-...', {
  allowedDomains: ['api.openai.com'],
})
 
const sandbox = await daytona.create({
  image: 'python:3.12-slim',
  secrets: ['OPENAI_API_KEY'],
})
cURL
# Store the secret once (write-only — the API never returns values)
curl -X PUT https://api.rl.trydaytona.com/secrets/OPENAI_API_KEY \
  -H "Authorization: Bearer $RLP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "sk-...", "allowed_domains": ["api.openai.com"]}'
 
# Attach it by name at create time
curl -X POST https://api.rl.trydaytona.com/vms \
  -H "Authorization: Bearer $RLP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image": "python:3.12-slim", "secrets": ["OPENAI_API_KEY"]}'

Choosing between them

Use env for configuration and secrets for anything whose leakage would matter. A simple test: if the sandbox will ever run code you did not write and fully trust, every credential belongs in secrets. The two compose freely — most real creates carry an env map for configuration alongside a secrets list for credentials.

See also

  • Secrets management — creating, updating, and deleting secrets; allowed_domains scoping; rotation.
  • Parameter reference — validation rules for env and secrets on the create request.

On this page