microsoft / microsoft/WSL

wslc: containers do not inherit image labels (Config.Labels missing), silently breaking VS Code Dev Containers Feature lifecycle hooks

Open
#41,152 4 comments 2 reactions 1 assignee View on GitHub

@beena352 is already working on this.

Since Aug 3, 2026.

feature wslc
Dominant language
C++
Stars
33.7k
Forks
1.8k
Avg merge
3d 17h
Merged PRs (30d)
116

Description

### Windows Version

Microsoft Windows [Version 10.0.26200.8893]

### WSL Version

2.9.4.0

### Are you using WSL 1 or WSL 2?

- [X] WSL 2
- [ ] WSL 1

### Kernel Version

6.18.35.2-1

### Distro Version

Ubuntu 24.04

### Other Software

- wslc 2.9.4.0 (`wslc version`)
- VS Code 1.130.0, Dev Containers extension 0.467.0 (affected downstream)
- `@devcontainers/cli` 0.88.0
- For comparison: Docker Engine (any recent version) exhibits the expected behavior

### Repro Steps

The repro is independent of VS Code / Dev Containers — three commands from a WSL distro (or the Windows side, with `wslc.exe`):

```sh
printf 'FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04\nLABEL test.label="hello-from-image"\n' > Dockerfile
wslc build -t label-test .
wslc run -d --name label-test-c label-test sleep infinity

# Image inspect: label is present
wslc inspect --type image label-test # -> Config.Labels contains "test.label": "hello-from-image"

# Container inspect: label is gone
wslc inspect label-test-c # -> Config.Labels is absent entirely
```

Cleanup: `wslc rm -f label-test-c; wslc rmi label-test`

### Expected Behavior

Containers created from an image inherit the image's labels, as with Docker Engine. `docker inspect ` shows `Config.Labels` populated with the image's labels merged with any `--label` flags (user-provided labels win on conflict).

References:

- Dockerfile reference (`LABEL`): "Labels included in base images (images in the `FROM` line) are inherited by your image." — https://docs.docker.com/reference/dockerfile/#label
- Docker Engine implementation: at container create, the daemon merges the image config into the container config, including labels — `mergeAndVerifyConfig` in [`daemon/create.go`](https://github.com/moby/moby/blob/master/daemon/create.go) calling `merge()` ([`daemon/commit.go`](https://github.com/moby/moby/blob/master/daemon/commit.go), the `imageConf.Labels` loop).

Expected `wslc inspect label-test-c` (excerpt):

```json
"Config": {
"Labels": {
"test.label": "hello-from-image",
"...": "(labels inherited from the base image)"
}
}
```

### Actual Behavior

`wslc inspect ` returns a `Config` object with **no `Labels` key at all** (observed keys: `Cmd`, `Entrypoint`, `Env`, `Healthcheck`, `StopTimeout`, `User`, `WorkingDir`). The top-level `Labels` field contains only labels passed explicitly via `--label` at `run`/`create` time. Image labels are never propagated to the container.

`wslc inspect --type image` does show the labels correctly, so the metadata survives `wslc build`; it is only the image-to-container inheritance step that is missing.

#### Real-world impact: VS Code Dev Containers silently loses Feature lifecycle hooks

The Dev Containers spec stores merged Feature metadata (including `onCreateCommand` / `postCreateCommand` contributed by Features) as a `devcontainer.metadata` **image label**, which tooling later reads back **from the container**:

- containers.dev spec: "The metadata is added to the image as a `devcontainer.metadata` label with a JSON string value" and it is "merged with any local devcontainer.json file contents at the time the container is created" — https://containers.dev/implementors/spec/#image-metadata
- `@devcontainers/cli` reads it via `containerDetails.Config.Labels['devcontainer.metadata']` and falls back to *empty metadata* when the label is absent — [`src/spec-node/imageMetadata.ts`, `getImageMetadataFromContainer`](https://github.com/devcontainers/cli/blob/main/src/spec-node/imageMetadata.ts)

The VS Code Dev Containers extension runs `devcontainer up --skip-post-create` and then executes lifecycle hooks in a separate `run-user-commands` phase that resolves hooks from the container's `devcontainer.metadata` label. With wslc, the label is invisible on the container, so all Feature-contributed lifecycle commands are **silently skipped** — no error, the container just comes up missing everything a Feature's `onCreateCommand` was supposed to install. I hit this in practice with a Nix devcontainer Feature: the container started, but `nix`/`direnv` were absent because `onCreateCommand` never ran; the marker files were written but no command executed.

This makes any devcontainer Feature that relies on lifecycle hooks (most non-trivial ones) unusable with wslc when opened through VS Code, in a way that is very hard for users to diagnose.

Note that wslc support was deliberately added to the Dev Containers tooling recently (`@devcontainers/cli` 0.88.0, [devcontainers/cli#1249](https://github.com/devcontainers/cli/pull/1249); Dev Containers extension 0.462.0+). That work adapts to several wslc differences (e.g. no `events` command → polling fallback, `--mount` → volume-arg conversion), but the Features metadata path still assumes Docker's image-label inheritance, so this incompatibility remains unhandled on the tooling side. I found no statement in the VS Code Dev Containers documentation, the WSL container documentation ([wsl-container](https://learn.microsoft.com/en-us/windows/wsl/wsl-container), [containers tutorial](https://learn.microsoft.com/en-us/windows/wsl/tutorials/wsl-containers)), or either project's changelog declaring label inheritance (or Feature lifecycle hooks) as a known limitation.

Prior-issue search: I reviewed the wslc-related issues in this repo (~50, e.g. #14432 volume-flag differences, #40957 bind mounts, #41123 image store) and in microsoft/vscode-remote-release (#11694 tracking, #11750, #11725, #11723) and devcontainers/cli — no existing report of missing image-label inheritance.

### Diagnostic Logs

_No response_

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.