Pinning VS Code extension versions in devcontainer.json
customizations.vscode.extensions installs the latest version of each extension by default, so two engineers creating the same DevContainer a week apart can get different linters, formatters, or debuggers — and a breaking extension update silently changes everyone’s environment. This page pins extensions to exact versions for reproducibility. It extends the Managing VS Code Extension Caches cluster.
Prerequisites
- VS Code with the Dev Containers extension, or the devcontainer CLI
- Extension IDs and the versions you want (from the Marketplace “Version History”)
How-To Steps
-
Append
@versionto each extension ID to pin it:{ "image": "mcr.microsoft.com/devcontainers/typescript-node:20@sha256:7c3e...", "customizations": { "vscode": { "extensions": [ "dbaeumer.vscode-eslint@3.0.10", "esbenp.prettier-vscode@11.0.0", "ms-python.python@2024.8.1" ] } }, "remoteUser": "vscode" }Verify: the Extensions view shows the pinned version, not “latest”.
-
Disable auto-update inside the container so a pinned extension is not silently upgraded:
{ "customizations": { "vscode": { "settings": { "extensions.autoUpdate": false, "extensions.autoCheckUpdates": false } } } } -
Cache the VSIX layer to avoid re-downloading pinned versions on every rebuild — covered by the parent cluster’s named-volume strategy:
{ "mounts": ["source=vscode-extensions,target=/home/vscode/.vscode-server/extensions,type=volume"] } -
Confirm the resolved set matches the pins across machines:
devcontainer read-configuration --workspace-folder . \ | jq '.configuration.customizations.vscode.extensions'
Common Pitfalls
| Symptom | Root Cause | Remediation |
|---|---|---|
| Extensions drift between engineers | IDs without @version install latest | Pin every extension to an exact version |
| Pinned extension upgrades itself | Auto-update enabled | Set extensions.autoUpdate and autoCheckUpdates to false |
| Re-download on every rebuild | Extensions in the ephemeral layer | Mount the server extensions dir as a named volume |
| Pin rejected as invalid | Version doesn’t exist for that extension | Copy the exact version from Marketplace Version History |
Conclusion
The invariant: every extension carries an explicit @version and auto-update is off, so the editor tooling is as reproducible as the image itself. Pin the versions, cache the VSIX layer, and confirm with read-configuration that every machine resolves the identical set.
FAQ
Does version pinning work in Codespaces too?
Yes. The @version syntax is honoured by Codespaces and the devcontainer CLI, not just local VS Code, so the pin is portable.
How do I find a valid version string?
Open the extension’s Marketplace page and use “Version History” — copy the exact version (e.g. 3.0.10) into the ID after an @.
Related
- Managing VS Code Extension Caches — the parent cluster on caching the VSIX layer.
- Speeding up VS Code extension installation in containers — install-time optimization for these pinned extensions.
- devcontainer.json Property Reference — the
customizations.vscodenamespace.