# How Iris Environment Variables Are Injected Into Docker Containers

> Learn how Iris injects environment variables into Docker containers. Discover the two-phase pipeline for build-time metadata and run-time configuration.

- Repository: [The Marin Project/marin](https://github.com/marin-community/marin)
- Tags: how-to-guide
- Published: 2026-08-29

---

**Iris injects environment variables into Docker containers through a two-phase pipeline that embeds immutable build-time metadata via Dockerfile directives and propagates dynamic run-time configuration via the `DockerRuntime` class.**

The marin-community/marin project implements a strict environment variable injection system to ensure containerized workers maintain provenance tracking while receiving live configuration from the controller. This dual-phase approach separates static image attributes from mutable deployment parameters, ensuring reproducibility across distributed clusters.

## Build-Time Injection via Dockerfile ARG and ENV

Iris bakes provenance metadata directly into container images during the build process. This guarantees that every image carries immutable identifiers regardless of where it executes.

### Defining Iris Metadata as Build Arguments

In `lib/iris/Dockerfile`, Iris declares build arguments between lines 193-201 to capture version control context. These declarations accept values passed during the Docker build process:

```dockerfile
ARG IRIS_GIT_HASH=unknown
ARG IRIS_PROVENANCE={}
ARG IRIS_REVISION_DATE=

```

When the Iris build pipeline executes, it passes specific commit hashes and build timestamps to these arguments. This establishes a permanent link between the running container and the exact source revision used to construct it.

### Promoting ARG to ENV Variables

Immediately after declaration, Iris promotes these build arguments to environment variables using the `ENV` directive. This conversion persists the values in the final image layer:

```dockerfile
ENV IRIS_GIT_HASH=${IRIS_GIT_HASH}
ENV IRIS_PROVENANCE=${IRIS_PROVENANCE}
ENV IRIS_REVISION_DATE=${IRIS_REVISION_DATE}

```

By promoting `ARG` to `ENV`, the metadata becomes queryable via `docker inspect` and accessible to applications running inside the container without requiring external configuration mounts.

## Run-Time Injection via DockerRuntime

While build-time variables capture static metadata, the `DockerRuntime` class in [`lib/iris/cluster/runtime/docker.py`](https://github.com/marin-community/marin/blob/main/lib/iris/cluster/runtime/docker.py) handles dynamic configuration when launching worker containers.

### Filtering IRIS_ Prefixed Variables

When `DockerRuntime.create_container()` executes, it filters the current process environment to identify Iris-specific configuration. The implementation iterates over `os.environ` and selects only keys beginning with the `IRIS_` prefix:

```python
env_vars = {
    key: os.getenv(key)
    for key in os.environ
    if key.startswith("IRIS_")
}

```

This selective propagation ensures the container receives essential runtime parameters—such as `IRIS_CONTROLLER_URL`, `IRIS_JOB_ENV`, and `IRIS_MULTIGPU_PROCESS_INDEX`—without inheriting unrelated host environment variables that could cause conflicts.

### Constructing the Docker Command

After collecting the filtered variables, `DockerRuntime` constructs the `docker run` command by expanding the environment dictionary into explicit `--env` flags:

```python
docker_cmd = [
    "docker", "run", "--rm",
    *(f"--env={k}={v}" for k, v in env_vars.items()),
    self.image,
    *cmd
]
subprocess.run(docker_cmd, check=True)

```

Each key-value pair becomes an explicit `--env KEY=VALUE` argument in the subprocess call. This approach ensures compatibility across Docker versions and prevents environment leakage through inherited shell contexts.

## Security and Configuration Boundaries

The injection mechanism respects security boundaries defined in [`lib/iris/config.py`](https://github.com/marin-community/marin/blob/main/lib/iris/config.py) via the `WorkerConfig` class. When `DOCKER_ACCESS` is enabled, the runtime mounts the Docker socket while maintaining strict control over which variables propagate.

Sensitive values such as API tokens travel through the same `IRIS_` prefixed channel, often consolidated under `IRIS_JOB_ENV`. Because `DockerRuntime` uses explicit command-line flags rather than environment files, these secrets remain scoped to the specific container process and do not persist in host shell histories.

## Complete Worker Launch Example

Combining both phases, launching an Iris worker container executes as follows:

```bash
docker run --rm \
  --env IRIS_CONTROLLER_URL=https://controller.example.com \
  --env IRIS_JOB_ENV='{"HF_TOKEN":"abc123"}' \
  --env IRIS_MULTIGPU_PROCESS_INDEX=0 \
  ghcr.io/marin-community/iris-worker:latest \
  python -m iris.task.main

```

In this invocation, `IRIS_CONTROLLER_URL` and `IRIS_JOB_ENV` originate from the controller's environment and pass through `DockerRuntime`, while the underlying image already contains `IRIS_GIT_HASH` from the build process documented in `lib/iris/Dockerfile`.

## Summary

- **Build-time injection** uses `ARG` and `ENV` directives in `lib/iris/Dockerfile` (lines 193-201) to bake provenance metadata into images at build time.
- **Run-time injection** relies on `DockerRuntime` in [`lib/iris/cluster/runtime/docker.py`](https://github.com/marin-community/marin/blob/main/lib/iris/cluster/runtime/docker.py) to filter `IRIS_` prefixed variables and append them as `--env` flags to `docker run` commands.
- The **WorkerConfig** class in [`lib/iris/config.py`](https://github.com/marin-community/marin/blob/main/lib/iris/config.py) governs which security contexts and variables should propagate to containers.
- The dual-phase approach separates immutable image metadata from dynamic configuration and secrets, ensuring reproducibility while maintaining operational flexibility.

## Frequently Asked Questions

### How does Iris distinguish between build-time and run-time environment variables?

Iris uses Dockerfile `ARG` and `ENV` directives for build-time data like `IRIS_GIT_HASH`, while the `DockerRuntime` class handles run-time variables by scanning for the `IRIS_` prefix in the current process environment. Build-time variables become permanent image attributes baked into the container layer, whereas run-time variables are injected fresh via `--env` flags with every container launch.

### Where does the DockerRuntime class locate the environment variables it injects?

`DockerRuntime` inspects the Python `os.environ` dictionary to source variables. According to the implementation in [`lib/iris/cluster/runtime/docker.py`](https://github.com/marin-community/marin/blob/main/lib/iris/cluster/runtime/docker.py), it specifically filters for keys starting with `IRIS_` to ensure only Iris-related configuration propagates to worker containers, preventing pollution from the host environment.

### Can I pass sensitive secrets through Iris environment variables?

Yes. Iris injects secrets via the same `IRIS_` prefixed mechanism, often consolidated under `IRIS_JOB_ENV`. Because `DockerRuntime` uses explicit `--env` flags generated at execution time rather than mounting environment files or using Docker's inherited environment feature, secrets remain scoped to the specific container process and do not persist on the host filesystem or in shell histories.

### What happens if an environment variable is defined both at build-time and run-time?

Run-time values take precedence because `DockerRuntime` explicitly sets them via `--env` flags when executing `docker run`. This allows the same base image to serve different controllers or job configurations without rebuilding, while the build-time metadata remains available as fallback provenance data visible through `docker inspect` or inside the container.