Automatic Package Installation
Code name:
package-auto-install
Automatically detects and runs npm/yarn/pnpm install in non-interactive mode after container creation.
Also included automatically: repairs broken host paths in your git config and restores your SSH commit-signing key on every attach, on both local and cloud containers, with nothing to set up on your end — see
helpers4-commonfor how it works.
Features
Section titled “Features”- Automatic detection: Detects package manager based on lockfile (pnpm-lock.yaml, yarn.lock, package-lock.json)
- Corepack support: Automatically installs and enables corepack if
packageManagerfield is found in package.json (required for Node 24+) - Non-interactive mode: Sets
CI=trueto avoid prompts (e.g., pnpm won’t ask to delete node_modules) - Smart command selection: Uses
npm ci,pnpm install --frozen-lockfile, oryarn install --immutablewhen lockfiles exist - Flexible configuration: Override package manager, command, and working directory
- Skip if exists: Optionally skip installation if node_modules already exists
- Multi-root support: Auto-discover VS Code/Cursor
.code-workspaceand IntelliJ.idea/modules.xmlproject files to install across all workspace folders
Basic Usage
Section titled “Basic Usage”Add this feature to your devcontainer.json:
This will:
- Detect the package manager from lockfile
- Run the appropriate install command automatically
- Set
CI=trueto prevent interactive prompts
Remove Manual postCreateCommand
Section titled “Remove Manual postCreateCommand”If you have this in your devcontainer.json, you can now remove it:
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
command | string | auto | Installation command: install, ci, or auto to detect |
packageManager | string | auto | Package manager: npm, yarn, pnpm, nub, or auto to detect. auto never resolves to nub — it’s explicit-only, since nub isn’t a lockfile format. nub runs nub install and requires the nub feature to also be installed. |
workingDirectory | string | /workspaces/${localWorkspaceFolderBasename}1 | Directory where to run install. Overridden by directories. Used as fallback scan root when autoDiscover finds no workspace files. |
skipIfNodeModulesExists | boolean | false | Skip if node_modules exists |
additionalArgs | string | "" | Additional arguments for install command |
directories | string | "" | Comma-separated list of directories to install in. Overrides workingDirectory and autoDiscover. |
autoDiscover | boolean | false | Scan /workspaces for VS Code/Cursor .code-workspace and IntelliJ .idea/modules.xml files and install in every discovered folder with a package.json. |
Examples
Section titled “Examples”Force specific package manager
Section titled “Force specific package manager”Use npm ci explicitly
Section titled “Use npm ci explicitly”Skip if node_modules exists (useful for rebuilds)
Section titled “Skip if node_modules exists (useful for rebuilds)”Pass additional arguments
Section titled “Pass additional arguments”Custom working directory
Section titled “Custom working directory”VS Code / Cursor multi-root workspace
Section titled “VS Code / Cursor multi-root workspace”When your devcontainer uses a .code-workspace file with multiple folders, enable autoDiscover to install packages in every discovered folder:
The feature scans /workspaces (depth 3) for *.code-workspace files, parses the folders[].path array, resolves relative paths, and runs the appropriate package manager in each folder that contains a package.json. Each folder may use a different package manager — detection runs independently per folder.
IntelliJ IDEA multi-module project
Section titled “IntelliJ IDEA multi-module project”autoDiscover: true also scans for .idea/modules.xml files (depth 4) and extracts module root directories from the filepath attributes.
Explicit list of directories
Section titled “Explicit list of directories”For any IDE that does not have a parseable workspace file (Zed, Neovim, etc.) or when you want precise control:
directories takes precedence over both workingDirectory and autoDiscover.
How It Works
Section titled “How It Works”Corepack Support (Node 24+)
Section titled “Corepack Support (Node 24+)”If your package.json contains a packageManager field (e.g., "packageManager": "pnpm@9.0.0"):
- The feature checks if corepack is available
- If not, it installs corepack globally with
npm install -g corepack - Enables corepack with
corepack enable - Corepack then automatically installs and uses the exact package manager version specified
Node 24+ dropped corepack from the default install, so this step is what makes the
packageManager field still work there without you installing corepack yourself.
Package Manager Detection
Section titled “Package Manager Detection”The feature detects the package manager in this order:
- From
packageManagerfield in package.json (highest priority)- Example:
"packageManager": "pnpm@9.0.0"→ uses pnpm - This is the most reliable and modern approach
- Example:
- From lockfiles:
pnpm-lock.yaml→ uses pnpmyarn.lock→ uses yarnpackage-lock.json→ uses npm
- Default: Falls back to npm if nothing is detected
Command Selection (when command: "auto")
Section titled “Command Selection (when command: "auto")”For each package manager:
- npm: Uses
npm ciif package-lock.json exists, otherwisenpm install - pnpm: Uses
pnpm install --frozen-lockfileif pnpm-lock.yaml exists, otherwisepnpm install - yarn:
- Yarn 2+: Uses
yarn install --immutableif yarn.lock exists - Yarn 1.x: Uses
yarn install --frozen-lockfileif yarn.lock exists
- Yarn 2+: Uses
CI Mode
Section titled “CI Mode”The feature sets CI=true environment variable, which:
- pnpm: Automatically removes old node_modules without prompting
- npm: Enables strict mode in
npm ci - yarn: Enables immutable installs
Execution Order
Section titled “Execution Order”The feature uses installsAfter to ensure it runs after:
ghcr.io/devcontainers/features/common-utilsghcr.io/devcontainers/features/node
The actual package installation runs in postCreateCommand, which is the last lifecycle hook after all features are installed.
Troubleshooting
Section titled “Troubleshooting”Installation not running
Section titled “Installation not running”Check that:
package.jsonexists in the working directory- The package manager is installed (use node feature or appropriate base image)
Wrong package manager detected
Section titled “Wrong package manager detected”Force the package manager explicitly:
Installation fails
Section titled “Installation fails”Check the container logs during creation. The feature will show the exact command being run and any errors.
Need to debug
Section titled “Need to debug”You can manually run the installation script:
Version History
Section titled “Version History”- v1.2.3: Documentation only, no functional change — the previous wording sweep made the
JSON
descriptionfield far too long, shifting focus away from the feature itself onto the self-heal side benefit. Shortened to 5 words and kept generic (no implementation detail like “git config”), matching the original’s brevity and level of detail. - v1.2.2: Documentation only, no functional change — the self-heal callout above (and the
JSON
descriptionfield) led with internal jargon (“helpers4’s self-heal”) instead of the actual benefit; reworded to lead with what it does, with the full mechanism staying inhelpers4-common’s own README. - v1.2.1: Documentation only, no functional change —
workingDirectory’s manifest default (/workspaces/${localWorkspaceFolderBasename}) andinstall.sh’s own fallback (plain/workspaces, used only when it runs standalone without the devcontainer CLI resolving that option) had diverged with no explanation. Both are intentional; added cross-referencing comments ininstall.shand a footnote here instead of unifying them. - v1.2.0: Documentation only, no functional change — mentions that
helpers4-common’s automatic git-config self-heal (see above) now comes along with this feature. - v1.1.0: Switched from an inline copy of
helpers4-common’s bootstrap (user detection, apt helpers) to a directdependsOnon thehelpers4-commonfeature — no behavior change, just a single source of truth for that logic instead of a copy every feature had to keep in sync. - v1.0.9: Added
nubtoinstallsAfter, now that thenubfeature is published — couldn’t be added in the same release asnubitself, sinceinstallsAfterhas to resolve from the registry andnubdidn’t exist there yet. Doesn’t change behavior (package-auto-install only callsnubviapostCreateCommand, which already runs after every feature’sinstall.shcompletes), just documents the relationship for anyone reading the manifest. - v1.0.8: Added
nubas apackageManagervalue — runsnub install(requires thenubfeature too, with its defaultinstallGlobally: true; nub only understands npm/pnpm/bun lockfiles, not yarn.lock).autodetection now validates thepackage.jsonpackageManagerfield against npm/pnpm/yarn before trusting it, so an unrelated value (includingnub, which doesn’t use that field’s convention) can’t makeautoresolve somewhere unexpected.command: ciis ignored when the resolved package manager isnub, since nub has nocisubcommand — it always runsnub install. - v1.0.2: Added
autoDiscover(scan VS Code/Cursor.code-workspaceand IntelliJ.idea/modules.xml) anddirectories(explicit comma-separated list) options for multi-root workspace support. Each folder runs package manager detection independently. - v1.0.1: Added corepack support for Node 24+ (
packageManagerfield in package.json). - v1.0.0: Initial release.
License
Section titled “License”LGPL-3.0 - See LICENSE file for details
Footnotes
Section titled “Footnotes”-
install.shitself falls back to plain/workspaces(not/workspaces/${localWorkspaceFolderBasename}) whenWORKINGDIRECTORYisn’t set in its environment. That’s intentional, not a mismatch to fix: the devcontainer CLI always resolves and passes the real default (or your override) at install time, soinstall.sh’s own fallback only ever applies when it runs standalone, outside that pipeline — e.g. local testing. ↩
