Environment Variables Reference¶
Modal Sandboxes uses a unified .env file for local development and control plane operations (templated in .env.example).
Configuration Variables¶
1. Control Plane & Dashboard Authentication¶
WEB_UI_USERNAME: Username for HTTP Basic Authentication on the web dashboard and REST API routes (POST /launch,POST /snapshot) (default:admin).WEB_UI_PASSWORD: Password for HTTP Basic Authentication on the web dashboard and REST API routes (POST /launch,POST /snapshot).
2. Modal Cloud Authentication¶
MODAL_TOKENS: JSON array string containing multi-account Modal profile objects with credentials (token_id,token_secret, optionalactive).
3. Source Control & GitHub¶
GH_TOKEN: GitHub Personal Access Token with repository read/clone permissions.GH_ORG_NAME: Default GitHub organization name (e.g.,sFactory0). When passing shorthand-r <repo>, the runner cloneshttps://github.com/${GH_ORG_NAME}/${repo}.
4. Remote Ingress & SSH¶
CLOUDFLARE_TUNNEL_TOKEN: Cloudflare Tunnel credential token for custom ingress domains.MODAL_SANDBOX_TUNNEL_DOMAIN: Optional custom hostname display format (e.g.3000.yourdomain.com).SSH_AUTHORIZED_KEY: Optional explicit SSH public key string to authorize in addition toshared/ssh-key.pub.
5. AI Tooling Providers¶
OPENCODE_GO_API_KEY: API key for OpenCode Go provider.ANTIGRAVITY_CLIENT_ID: OAuth client ID for Google Antigravity.ANTIGRAVITY_CLIENT_SECRET: OAuth client secret for Google Antigravity.ANTIGRAVITY_TOKENS: JSON array string containing Antigravity user profile credentials.
6. Encryption & SOPS¶
AGE_PRIVATE_KEY: Age private key (AGE-SECRET-KEY-...) used by SOPS to decrypt repository environment secrets.
7. Software Factory Provenance (In-Sandbox & Control Plane)¶
SOFTWARE_FACTORY_COMMIT: Full 40-character Git commit SHA of thesoftware-factorycheckout from which the sandbox was created.SOFTWARE_FACTORY_COMMIT_SHORT: 7-character short commit SHA.SOFTWARE_FACTORY_REF: Git branch or tag name of thesoftware-factorycheckout (e.g.main).SOFTWARE_FACTORY_DIRTY: Boolean indicator ("1"or"0") signaling whether the host checkout had uncommitted modifications at the time of sandbox creation.
8. Git Identity (Default Authorship Fallback)¶
DEFAULT_GIT_NAME: Default Git author and committer name applied globally across sandbox containers when repository-specific authorship is not specified.DEFAULT_GIT_EMAIL: Default Git author and committer email address applied globally across sandbox containers when repository-specific authorship is not specified.
Resolution Order & Precedence¶
Modal Sandboxes resolves environment variables through a deterministic cascade across configuration files, host environment variables, and remote secret vaults.
1. Host Environment Whitelist¶
When bundling secrets for Modal injection (sandbox/function.py and sandbox/launcher.py), the runner does not forward the host's entire os.environ. Instead, it samples only an explicit whitelist:
WEB_UI_USERNAME, WEB_UI_PASSWORD, GH_TOKEN, GH_ORG_NAME, CLOUDFLARE_TUNNEL_TOKEN, MODAL_SANDBOX_TUNNEL_DOMAIN, SSH_AUTHORIZED_KEY, OPENCODE_GO_API_KEY, ANTIGRAVITY_CLIENT_ID, ANTIGRAVITY_CLIENT_SECRET, ANTIGRAVITY_TOKENS, AGE_PRIVATE_KEY, MODAL_TOKENS.
Why a whitelist? Sweeping the entire host environment would bundle sensitive host credentials (such as AWS keys, personal API tokens, SSH agent variables, or shell history) and leak them into remote container environments.
2. Precedence Rules: .env vs. Host os.environ¶
The conflict resolution order depends on the functional role of the credential:
| Credential Type | Precedence Rule | Rationale |
|---|---|---|
Control Plane Auth (WEB_UI_USERNAME, WEB_UI_PASSWORD) |
Host os.environ WINS over .env |
Allows operators to supply ad-hoc credentials via CLI or CI/CD pipelines without modifying tracked local files. |
Tooling & Service Secrets (GH_TOKEN, AGE_PRIVATE_KEY, etc.) |
.env file WINS over Host os.environ |
Guarantees reproducible local runs from the verified project .env file, using host environment variables solely as fallbacks for missing keys. |
Child Sandbox Launching (sandbox/launcher.py) |
.env file used exclusively if present |
If .env exists on the local machine, modal.Secret.from_dotenv(".env") bundles it directly. In container or CI environments lacking .env, it falls back to the whitelisted keys in os.environ. |
3. Multi-Source Lookup Hierarchy¶
If a configuration value is not resolved in the primary .env file or host environment, the runner consults secondary sources:
flowchart TD
Start["Variable Lookup"] --> CheckDotEnv{".env File?"}
CheckDotEnv -- Yes --> ReadDotEnv["Read .env Key"]
CheckDotEnv -- No --> CheckEnv{Host os.environ?}
CheckEnv -- Yes (Whitelisted) --> ReadEnv["Read os.environ Key"]
CheckEnv -- No --> SecondarySources{"Check Secondary Sources"}
SecondarySources --> AuthFallback["1. Legacy .secrets/.env & secrets/.env (Auth fallback)"]
SecondarySources --> ModalConfig["2. ~/.modal.toml (Modal profile tokens)"]
SecondarySources --> AgeKeyFile["3. ~/.config/sops/age/keys.txt (Age private key)"]
SecondarySources --> SSHPubFile["4. shared/ssh-key.pub (SSH public key)"]
SecondarySources --> EnvStore["5. github.com/${GH_ORG_NAME}/env-store (Target repo secrets)"]
SecondarySources --> RuntimeGen["6. secrets.token_urlsafe(16) (OPENCODE_SERVER_PASSWORD)"]
- Legacy Directory Fallback Files:
root / ".secrets" / ".env"androot / "secrets" / ".env"are checked ifWEB_UI_USERNAMEorWEB_UI_PASSWORDare absent from root.env.- User Configuration Files:
~/.modal.toml(or$MODAL_CONFIG_PATH): Parsed bysandbox/profiles.pyfor Modal account workspace profiles.~/.config/sops/age/keys.txt: Parsed bysandbox/env_store.pyfor SOPS Age private keys if not provided viaAGE_PRIVATE_KEY.shared/ssh-key.pub: Read bysandbox/launcher.pyifSSH_AUTHORIZED_KEYis not explicitly set.- Remote Secret Vault (
env-store): - For repository sandboxes (
-r <repo>), target repository environment configurations are fetched directly from the privategithub.com/${GH_ORG_NAME}/env-storerepository usingGH_TOKENand decrypted host-side. - Dynamic Runtime Generation:
- Ephemeral passwords like
OPENCODE_SERVER_PASSWORDare generated cryptographically at launch usingsecrets.token_urlsafe(16)and injected directly into the target sandbox.