- VSCode Version: 1.118.1 (user setup), commit 034f571df509819cc10b0c8129f66ef77a542f0e
- Local OS Version: Windows 11
- Remote OS Version: Ubuntu 24.04.3 LTS
- Remote Extension/Connection Type: Dev Containers
- Logs:
Steps to Reproduce:
- In VS Code user settings, configure
dev.containers.defaultFeatures with one or more features that you intentionally do NOT want declared in the project's devcontainer.json (the entire purpose of this user-level setting):
"dev.containers.defaultFeatures": {
"ghcr.io/example/feature-a:latest": {},
"ghcr.io/example/feature-b:latest": {}
}
- Open a project whose
.devcontainer/devcontainer.json does NOT declare those features.
- Build/rebuild the dev container (lockfile generation is on by default since 1.118: https://code.visualstudio.com/updates/v1_118#_dev-container-lockfile-for-features-enabled-by-default ).
- Inspect
.devcontainer/devcontainer-lock.json.
Expected:
per the devcontainer-lockfile spec, the lockfile records "each feature listed in the devcontainer.json". User-level Default Features are not part of the project manifest and should not appear in the project's lockfile.
Actual:
devcontainer-lock.json contains entries for the user-level Default Features alongside the features declared in devcontainer.json. Example excerpt (only node:2.0.0 is declared in devcontainer.json; the rest come from the user setting):
{
"features": {
"ghcr.io/devcontainers/features/node:2.0.0": { "version": "2.0.0", "resolved": "...", "integrity": "..." },
"ghcr.io/example/feature-a:latest": { "version": "1.0.0", "resolved": "...", "integrity": "..." },
"ghcr.io/example/feature-b:latest": { "version": "1.0.1", "resolved": "...", "integrity": "..." }
}
}
Impact:
the whole reason to put a feature in dev.containers.defaultFeatures (instead of devcontainer.json) is that it is personal: the user wants it in their own dev containers without committing it to the repo. With lockfile generation default-enabled in 1.118+, those personal features now appear in devcontainer-lock.json, which is a project-level file expected to be committed (it lives next to devcontainer.json and exists for build reproducibility). This silently leaks personal tooling choices into PRs and breaks the user-vs-project separation that the Default Features setting was designed to provide.
Suggested fix:
scope the lockfile strictly to features declared in devcontainer.json (matching the spec). Features merged from dev.containers.defaultFeatures or other user-level sources should still be installed but excluded from devcontainer-lock.json.
Workarounds considered:
- Disable lockfile generation per-project: no documented project-level opt-out as of writing.
- Add
.devcontainer/devcontainer-lock.json to .gitignore: loses the supply-chain pinning benefit for legitimately-declared features too.
- Move Default Features out of user settings into
devcontainer.json: defeats the purpose of the setting.
None are satisfactory.
References:
Does this issue occur when you try this locally?: N/A (Dev Containers-only behavior)
Does this issue occur when you try this locally and all extensions are disabled?: N/A
Steps to Reproduce:
dev.containers.defaultFeatureswith one or more features that you intentionally do NOT want declared in the project'sdevcontainer.json(the entire purpose of this user-level setting):.devcontainer/devcontainer.jsondoes NOT declare those features..devcontainer/devcontainer-lock.json.Expected:
per the devcontainer-lockfile spec, the lockfile records "each feature listed in the
devcontainer.json". User-level Default Features are not part of the project manifest and should not appear in the project's lockfile.Actual:
devcontainer-lock.jsoncontains entries for the user-level Default Features alongside the features declared indevcontainer.json. Example excerpt (onlynode:2.0.0is declared indevcontainer.json; the rest come from the user setting):{ "features": { "ghcr.io/devcontainers/features/node:2.0.0": { "version": "2.0.0", "resolved": "...", "integrity": "..." }, "ghcr.io/example/feature-a:latest": { "version": "1.0.0", "resolved": "...", "integrity": "..." }, "ghcr.io/example/feature-b:latest": { "version": "1.0.1", "resolved": "...", "integrity": "..." } } }Impact:
the whole reason to put a feature in
dev.containers.defaultFeatures(instead ofdevcontainer.json) is that it is personal: the user wants it in their own dev containers without committing it to the repo. With lockfile generation default-enabled in 1.118+, those personal features now appear indevcontainer-lock.json, which is a project-level file expected to be committed (it lives next todevcontainer.jsonand exists for build reproducibility). This silently leaks personal tooling choices into PRs and breaks the user-vs-project separation that the Default Features setting was designed to provide.Suggested fix:
scope the lockfile strictly to features declared in
devcontainer.json(matching the spec). Features merged fromdev.containers.defaultFeaturesor other user-level sources should still be installed but excluded fromdevcontainer-lock.json.Workarounds considered:
.devcontainer/devcontainer-lock.jsonto.gitignore: loses the supply-chain pinning benefit for legitimately-declared features too.devcontainer.json: defeats the purpose of the setting.None are satisfactory.
References:
Does this issue occur when you try this locally?: N/A (Dev Containers-only behavior)
Does this issue occur when you try this locally and all extensions are disabled?: N/A