Installs @devcontainers/cli as /usr/local/bin/devcontainer, backed by a private, sha256-verified Node.js under /opt/devcontainer-cli that is NOT added to PATH - so devcontainer up/build/exec/features test work inside a container without a system-wide Node.js. Pair with docker-in-docker (needed for features test and nested up on Windows/macOS hosts) or docker-outside-of-docker (Linux hosts, same paths).
"features": {
"ghcr.io/nabil-becker/devcontainer-features/devcontainer-cli:1": {}
}| Options Id | Description | Type | Default Value |
|---|---|---|---|
| installTaskfile | Also install this Feature's Taskfile into /usr/local/share/go-task/includes.d so its tasks appear under the : namespace of a root Taskfile that includes /usr/local/share/go-task/features.yml (see the go-task Feature). | boolean | true |
| version | @devcontainers/cli npm version to install (e.g. '0.89.0'), or 'latest'. | string | latest |
| nodeVersion | Node.js release used privately by the CLI. Downloaded from nodejs.org and verified against its SHASUMS256.txt. | string | 24.21.0 |
The @devcontainers/cli npm package, run on a private Node.js build, so
devcontainer up, build, exec and features test work inside a
container with no system-wide Node.js. install.sh runs
devcontainer-cli.sh install, which:
- Downloads
node-v<nodeVersion>-linux-<arch>.tar.gzandSHASUMS256.txtfromhttps://nodejs.org/dist/v<nodeVersion>/, checks the tarball's SHA-256 against that file, and unpacks it to/opt/devcontainer-cli/node. - Runs
npm install -g --prefix /opt/devcontainer-cli @devcontainers/cli(with@<version>unlessversionislatest), using that Node. - Writes a two-line wrapper at
/usr/local/bin/devcontainerthat runs the CLI with the privatenode, then runsdevcontainer --versionso the version is in the build log.
node and npm are deliberately not on PATH afterwards. Only the
devcontainer wrapper knows about them, so the Feature cannot collide with
the Node.js toolchain your project pins. It installs no Docker engine
(see Composition). The directory /opt/devcontainer-cli is removed and
rebuilt on every install, and npm's cache goes to a temporary directory that
is deleted, so it does not stay in the image layer.
What is verified, and what is not:
- The private Node.js is verified: a missing line in
SHASUMS256.txtor a mismatch fails the build. - The CLI itself is NOT checked against a checksum. It comes from
npm install -gand is protected only by npm's own integrity metadata for the package and its dependencies. This Feature adds no checksum, no signature check and no lockfile for it. The root README names this as the one exception to "verifies what it downloads".
curl, ca-certificates and tar are installed with apt-get only when
missing. The install must run as root.
version(defaultlatest): the@devcontainers/clinpm version, for example0.89.0; no leadingv.latestis whatever npm calls latest at image build time (not thereleases/latestredirect the other installers use), so two builds a month apart can differ. Pin it when you wantdevcontainer features testorupto behave the same in CI and on every laptop, or when a new CLI release breaks something.nodeVersion(default24.21.0): the Node.js release the CLI runs on, asx.y.zwithoutv. Change it only when a pinned CLI version needs a newer or older Node than the default (the CLI states its Node range), or when nodejs.org no longer serves the default. It does not change the Node.js your project uses; that one is separate.installTaskfile(defaulttrue): set it tofalsewhen you do not use the go-task registry and do not want/usr/local/share/go-task/includes.d/devcontainer-cli.ymlin the image.
There is no run-time override; both versions are fixed at build.
No state: installs binaries only. No volume, no containerEnv, no
post-create hook.
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/nabil-becker/devcontainer-features/devcontainer-cli:1": {
"version": "0.89.0"
}
}Drop version to follow npm's latest at each image build.
devcontainer --version # prints the CLI version, for example 0.89.0
task devcontainer-cli:versionThe build log ends the install with the same devcontainer --version
line. command -v node finds nothing, on purpose. There is no post-create
line: nothing runs after the build.
installsAfterorders it behind this collection'sgo-taskFeature (so the Taskfile registry exists and is refreshed), the officialcommon-utils, and the two official Docker Features (docker-in-docker,docker-outside-of-docker) so a Docker engine is in place before it when you use one.- It needs a Docker daemon for anything that builds or runs containers
(
up,build,features test);read-configurationand--versiondo not. It does not install one. Pair it with one of the official Features:
| Host | Use | Why |
|---|---|---|
| Windows / macOS (Docker Desktop) | docker-in-docker |
features test and nested devcontainer up bind-mount paths from inside this container; only a daemon that shares its filesystem can resolve them. |
| Linux (same paths on host and container) | docker-outside-of-docker |
Lighter, shares the host's image cache; works while the workspace is mounted at the same absolute path. |
- It augments no official Feature and replaces none. It is independent of
kube-state,gitand the other Features here.
With installTaskfile on, and a root Taskfile that includes the go-task
registry, these appear under devcontainer-cli::
devcontainer-cli:version: show the installed CLI version.devcontainer-cli:config: print the resolved and merged configuration ofWORKSPACE(default.) withdevcontainer read-configuration.devcontainer-cli:features:test: rundevcontainer features testfor the Features repository atPROJECT(default.); extra flags go after--(for example-f <id>). Needs a Docker daemon.
These are for using the CLI inside the container. To drive a devcontainer
from a host, use the devcontainer:* tasks (next section).
- Linux only in the container. The architectures are
x86_64/amd64,aarch64/arm64andarmv7l; any otheruname -mfails withunsupported architecture. ... not found in SHASUMS256.txtorSHA-256 mismatch: thenodeVersiondoes not exist for your architecture, or the download was corrupted. Fix the version or retry. The Node.js is never unpacked unchecked.- A failing
npm install(no network, or aversionthat npm does not have) fails the build. Check the version withnpm view @devcontainers/cli versionson a machine that has npm. features testand nestedupfail with a Docker error when no daemon is reachable; adddocker-in-docker(ordocker-outside-of-dockeron Linux). Withdocker-outside-of-docker, a workspace mounted at a different path on the host than inside the container cannot be resolved.- The CLI's own dependencies are resolved by npm at build time and are not locked here, so supply-chain trust in them is npm's, not this Feature's.
The same devcontainer-cli.sh that installs this Feature also has init
and run modes for a Linux/macOS host, where it caches a portable Node.js
under ~/.cache/devcontainer-features (or just uses a devcontainer already
on PATH when the version is latest). Together with the PowerShell flavour
for Windows it backs the task devcontainer:* tasks that
bootstrap/bootstrap.ps1 / bootstrap.sh vendor into a repo; nothing is
installed globally on any host. Its host settings are
DEVCONTAINER_DOCKER_PATH, DEVCONTAINER_NODE_VERSION and
DEVCONTAINER_CLI_VERSION.
task devcontainer:up (alias start) and task devcontainer:stop (alias
down) are idempotent, so other tasks can require them. up starts a stopped
container and leaves a running one alone. exec, shell, pwsh and claude
run up first; rebuild runs stop first (shutdown check included; FORCE=1 and
STOP_CHECK pass through) and then recreates the container; a stop that fails
(the check refuses, or any error) prints one line and the rebuild continues.
build and config touch no container.
stop is the one host task that is not a CLI subcommand (the CLI has none):
the run stop mode (and Stop-Devcontainer in the PowerShell module) finds
the workspace's container by the devcontainer.local_folder label that
devcontainer up sets, and stops it through the configured engine
(DEVCONTAINER_DOCKER_PATH). run running (and Get-RunningDevcontainerId)
is the same lookup as a silent test: exit 0 when a container runs, 1 when none
does, 2 when the engine cannot be asked; the running task wraps it. stop
asks it first: with no running container it prints "already stopped" and
succeeds, without running the check and without starting anything. Otherwise
the task runs the STOP_CHECK command inside the container (task var,
environment variable or .devcontainer/.env; default devhost-shutdown-check
of the devhost Feature, ADR 0015) and refuses to stop when it fails; FORCE=1
skips the check. The check goes through an internal exec that does not run up,
so a stop never starts a container. The check is in the Taskfile, so both host
paths honour it. The engine's ps --filter label=... and stop are assumed to
behave as Docker's; this is not tested against wslc or Podman. Stopping the
container any other way is not guarded.
The Dev Container CLI is published by the Dev Containers project as an npm
package, and its documented install is npm install -g @devcontainers/cli,
which needs a Node.js on PATH. This repository knows of no published Dev
Container Feature that installs it, so there is none to compare. This Feature
exists so that a container can run the CLI without a system-wide Node.js, and
so the same script serves the container and the host-side tasks.
Note: This file was auto-generated from the devcontainer-feature.json. Add additional notes to a NOTES.md.