Summary
When building a template from a Docker image that defines ENV variables (e.g. ENV PYTHONPATH=/app), those variables are correctly used during template build (RUN commands), but are not available to processes started at runtime via the Process.Start RPC.
Analysis
I traced the environment variable flow through the codebase and found:
Build time — works correctly
Docker image ENV vars are parsed from the OCI config into metadata.Context.EnvVars, and each RUN command explicitly passes them via ProcessConfig.Envs:
// packages/orchestrator/pkg/template/build/sandboxtools/command.go
envs := maps.Clone(metadata.EnvVars)
runCmdReq := connect.NewRequest(&process.StartRequest{
Process: &process.ProcessConfig{
Cmd: "/bin/bash",
Args: []string{"-l", "-c", command},
Envs: envs,
},
})
Runtime — ENV vars are lost
When a sandbox is created at runtime, the orchestrator sends env vars to envd via POST /init:
// packages/orchestrator/pkg/sandbox/envd.go
jsonBody := &envd.PostInitJSONBody{
EnvVars: s.Config.Envd.Vars, // ← only user-provided env vars from SDK
...
}
s.Config.Envd.Vars comes from the gRPC SandboxConfig.EnvVars, which originates from the API's body.EnvVars — these are user-provided env vars only. The template metadata's context.env_vars (containing Docker image ENV) does not appear to be merged into this path.
Inside envd, child process environment is built explicitly in handler.New:
// packages/envd/internal/services/process/handler/handler.go
formattedVars = append(formattedVars, "PATH="+os.Getenv("PATH"))
formattedVars = append(formattedVars, "HOME="+user.HomeDir)
// ... defaults.EnvVars (from MMDS + /init) ...
// ... per-request envs ...
cmd.Env = formattedVars
Only PATH is taken from os.Getenv(). Other Docker image ENV vars are not in defaults.EnvVars and are therefore absent from child processes.
Also, envd does not read /etc/environment or any filesystem-based env config at startup.
Concrete example
FROM python:3.11
ENV MY_APP_CONFIG=/etc/myapp/config.json
ENV PYTHONPATH=/app/lib
After building a template from this image:
- RUN echo $MY_APP_CONFIG during build → works ✓
- sdk.process.start("echo $MY_APP_CONFIG") at runtime → empty ✗
Question
-
Is this the intended behavior? If so, what is the design rationale for not propagating Docker image ENV to runtime processes?
-
Is there a recommended workaround? Should users manually pass all Docker image ENV vars via the SDK's envVars parameter when creating a sandbox?
Summary
When building a template from a Docker image that defines ENV variables (e.g. ENV PYTHONPATH=/app), those variables are correctly used during template build (RUN commands), but are not available to processes started at runtime via the Process.Start RPC.
Analysis
I traced the environment variable flow through the codebase and found:
Build time — works correctly
Docker image ENV vars are parsed from the OCI config into metadata.Context.EnvVars, and each RUN command explicitly passes them via ProcessConfig.Envs:
Runtime — ENV vars are lost
When a sandbox is created at runtime, the orchestrator sends env vars to envd via POST /init:
s.Config.Envd.Vars comes from the gRPC SandboxConfig.EnvVars, which originates from the API's body.EnvVars — these are user-provided env vars only. The template metadata's context.env_vars (containing Docker image ENV) does not appear to be merged into this path.
Inside envd, child process environment is built explicitly in handler.New:
Only PATH is taken from os.Getenv(). Other Docker image ENV vars are not in defaults.EnvVars and are therefore absent from child processes.
Also, envd does not read /etc/environment or any filesystem-based env config at startup.
Concrete example
After building a template from this image:
Question
Is this the intended behavior? If so, what is the design rationale for not propagating Docker image ENV to runtime processes?
Is there a recommended workaround? Should users manually pass all Docker image ENV vars via the SDK's envVars parameter when creating a sandbox?