Skip to content

Feature-contributed mounts skip variable substitution — ${devcontainerId} reaches Docker literally #52

@bilby91

Description

@bilby91

Symptom

Container create fails when a feature contributes a mount whose source uses a substitution variable. Observed with the docker-in-docker feature, which declares:

"mounts": [{
  "source": "dind-var-lib-docker-${devcontainerId}",
  "target": "/var/lib/docker",
  "type": "volume"
}]

Docker daemon rejects the create:

create dind-var-lib-docker-${devcontainerId}: "dind-var-lib-docker-${devcontainerId}" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed.

The literal ${devcontainerId} reaches ContainerCreate because it was never substituted.

Root cause

  1. config/resolve.go:190substituteAll runs as part of ResolveBytes. This correctly resolves ${devcontainerId} in the user's devcontainer.json mounts.
  2. up.go:264, up.go:278, up.go:485 — later, applyMetadataMergeconfig.MergeMetadata appends feature-contributed mounts to cfg.Mounts (config/merge_metadata.go:76-81).
  3. No substitution runs after the merge. Feature mount sources keep their literal ${devcontainerId} and flow straight into the ContainerCreate payload.

Same risk exists for any other string field a feature/base-metadata layer can contribute that may include substitution variables (containerEnv, remoteEnv, lifecycle hooks).

Suggested fix

Two shapes:

  • Local fix: in applyMetadataMerge (up.go:897), after MergeMetadata, run a substitution pass over the merged-in fields. Rebuild a SubstitutionContext from cfg.DevcontainerID, cfg.LocalWorkspaceFolder, cfg.ContainerWorkspaceFolder, and the same localEnv ResolveBytes saw.
  • Cleaner: have MergeMetadata accept a SubstitutionContext and substitute as it folds layers in, preserving the invariant that every string in ResolvedConfig is host-substituted.

Leaning toward the second so callers don't have to remember.

Repro

Any devcontainer that depends on the docker-in-docker feature (e.g. dap-workspace-55 in dap-ephraim-sandbox).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions