# NousResearch/hermes-agent · Docker container env vars silently ignored in config

## Open now

- 0 issues, 1 PR open on GitHub.
- PR #12825: fix(docker): forward configured environment variables to sandbox containers

Multiple reports and proposed fixes around docker_forward_env and docker_env config keys being dropped or never forwarded when creating Docker sandbox containers. The members describe the same bug from several angles along with various fix attempts.

## TL;DR

- 15 member(s): 0 open issue(s), 5 closed issue(s), 1 open PR(s), 2 merged PR(s), 7 PR(s) closed without merging.
- A fix was merged to main in PR #22852. Verify it addresses your case.
- **Recommended:** read PR #22852 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-22852`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.11`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #2878: fix(docker): docker_forward_env config key silently ignored — missing from container_config dict
- #4916: \[Bug\]: terminal_tool drops docker_forward_env when creating container sandboxes
- #5722: terminal.docker_forward_env and terminal.docker_env are silently ignored from config.yaml
- #12534: Docker sandbox never receives env vars from docker_forward_env: missing keys in container_config
- #20537: Bug: docker_env configuration is completely ignored (env_mappings missing docker_env)

## Prior & competing PRs

- **#2881: fix(docker): include docker_forward_env in container_config dict**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#4917: fix(terminal): pass docker_forward_env through terminal tool**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `db86ed199082d168546179d1c9200467039927bb`, `#4916`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#4972: fix: include docker_forward_env in terminal_tool container_config**. closed without merge · fix tier `suggested`
  - maintainer direction: category `merged_via`; implication `superseded`; refs `db86ed199082d168546179d1c9200467039927bb`, `#4916`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#14235: fix(terminal): forward docker_forward_env and docker_env to container_config (salvage #10162)**. merged to main · fix tier `suggested`
  - maintainer direction: category `merged`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#22852: fix(terminal): bridge docker_env config to TERMINAL_DOCKER_ENV (salvage #22626)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `cli.py`, `gateway/run.py`, `hermes_cli/config.py`, `tests/tools/test_terminal_config_env_sync.py`, `tools/terminal_tool.py`
  - fix hunks:
  - `cli.py`

    ```diff
    @@ -517,6 +517,7 @@ def load_cli_config() -> Dict[str, Any]:
             "container_disk": "TERMINAL_CONTAINER_DISK",
             "container_persistent": "TERMINAL_CONTAINER_PERSISTENT",
             "docker_volumes": "TERMINAL_DOCKER_VOLUMES",
    +        "docker_env": "TERMINAL_DOCKER_ENV",
             "docker_mount_cwd_to_workspace": "TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE",
             "docker_run_as_host_user": "TERMINAL_DOCKER_RUN_AS_HOST_USER",
             "sandbox_dir": "TERMINAL_SANDBOX_DIR",
    ```

    ```diff
    @@ -540,7 +541,7 @@ def load_cli_config() -> Dict[str, Any]:
                     continue
                 if _file_has_terminal_config or env_var not in os.environ:
                     val = terminal_config[config_key]
    -                if isinstance(val, list):
    +                if isinstance(val, (list, dict)):
                         os.environ[env_var] = json.dumps(val)
                     else:
                         os.environ[env_var] = str(val)
    ```
  - `gateway/run.py`

    ```diff
    @@ -460,6 +460,7 @@ def _reload_runtime_env_preserving_config_authority() -> None:
                     "container_disk": "TERMINAL_CONTAINER_DISK",
                     "container_persistent": "TERMINAL_CONTAINER_PERSISTENT",
                     "docker_volumes": "TERMINAL_DOCKER_VOLUMES",
    +                "docker_env": "TERMINAL_DOCKER_ENV",
                     "docker_mount_cwd_to_workspace": "TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE",
                     "docker_run_as_host_user": "TERMINAL_DOCKER_RUN_AS_HOST_USER",
                     "sandbox_dir": "TERMINAL_SANDBOX_DIR",
    ```

    ```diff
    @@ -478,7 +479,7 @@ def _reload_runtime_env_preserving_config_authority() -> None:
                         # receives a literal "~/" which the kernel rejects.
                         if _cfg_key == "cwd" and isinstance(_val, str):
                             _val = os.path.expanduser(_val)
    -                    if isinstance(_val, list):
    +                    if isinstance(_val, (list, dict)):
                             os.environ[_env_var] = json.dumps(_val)
                         else:
                             os.environ[_env_var] = str(_val)
    ```
  - `hermes_cli/config.py`

    ```diff
    @@ -4843,6 +4843,7 @@ def set_config_value(key: str, value: str):
             "terminal.vercel_runtime": "TERMINAL_VERCEL_RUNTIME",
             "terminal.docker_mount_cwd_to_workspace": "TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE",
             "terminal.docker_run_as_host_user": "TERMINAL_DOCKER_RUN_AS_HOST_USER",
    +        "terminal.docker_env": "TERMINAL_DOCKER_ENV",
             # terminal.cwd intentionally excluded — CLI resolves at runtime,
             # gateway bridges it in gateway/run.py. Persisting to .env causes
             # stale values to poison child processes.
    ```
  - `tests/tools/test_terminal_config_env_sync.py`

    ```diff
    @@ -208,3 +208,19 @@ def test_docker_mount_cwd_to_workspace_is_bridged_everywhere():
         assert "docker_mount_cwd_to_workspace" in _gateway_env_map_keys()
         assert "docker_mount_cwd_to_workspace" in _save_config_env_sync_keys()
         assert "TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE" in _terminal_tool_env_var_names()
    +
    +
    +def test_docker_env_is_bridged_everywhere():
    +    """Regression pin for docker_env config key being silently ignored.
    +
    +    ``terminal.docker_env`` in config.yaml specifies extra env vars to inject
    +    into the Docker container at runtime.  The key was present in
    +    _create_environment's container_config consumer (line ~1130) but never
    +    bridged from config.yaml to TERMINAL_DOCKER_ENV, so the dict was always
    +    empty regardless of what the user set.  Guard all four bridging points so
    +    this cannot regress.
    +    """
    +    assert "docker_env" in _cli_env_map_keys()
    +    assert "docker_env" in _gateway_env_map_keys()
    +    assert "docker_env" in _save_config_env_sync_keys()
    +    assert "TERMINAL_DOCKER_ENV" in _terminal_tool_env_var_names()
    ```
  - `tools/terminal_tool.py`

    ```diff
    @@ -1085,6 +1085,7 @@ def _get_env_config() -> Dict[str, Any]:
             "container_disk": _parse_env_var("TERMINAL_CONTAINER_DISK", "51200"),        # MB (default 50GB)
             "container_persistent": os.getenv("TERMINAL_CONTAINER_PERSISTENT", "true").lower() in ("true", "1", "yes"),
             "docker_volumes": _parse_env_var("TERMINAL_DOCKER_VOLUMES", "[]", json.loads, "valid JSON"),
    +        "docker_env": _parse_env_var("TERMINAL_DOCKER_ENV", "{}", json.loads, "valid JSON"),
             "docker_run_as_host_user": os.getenv("TERMINAL_DOCKER_RUN_AS_HOST_USER", "false").lower() in ("true", "1", "yes"),
         }
     
    ```
- **#2879: fix(docker): include docker_forward_env in container_config dict**. closed without merge
  - maintainer direction: category `stale_abandoned`; implication `active`
- **#12825: fix(docker): forward configured environment variables to sandbox containers**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `tests/tools/test_docker_forward_env.py`, `tools/environments/docker.py`
- **#17620: fix(execute_code): forward docker_forward_env into container_config**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`
- **#20549: fix(docker): bridge terminal docker_env config**. closed without merge
  - maintainer direction: category `uncategorized`; implication `needs_llm`
- **#22626: fix(terminal): bridge docker_env config to TERMINAL_DOCKER_ENV**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#22852`

## Fix status

- Merged to main in PR #22852 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-10`.
- Subsystem: `environments`
