Self-hosted service
devcontainers/images avatar
devcontainers/images

devcontainers/images and the case for a small, curated set of base images

Repository for pre-built dev container images published under mcr.microsoft.com/devcontainers

2,131 stars966 forksShellMIT

At a glance

What is it?
This repository publishes the pre-built container images that the Dev Container specification is designed to consume, built on top of a features system rather than a growing catalogue. The engineering decisions worth understanding are the deliberate layer hygiene in the Dockerfiles and the two-layer licensing, and the practical question is what happens when your stack is not in the select set.
Who is it for?
Use these images when your stack is in the published set and you want a development environment that starts without a build step, then pin a version tag and record the digest so your container is reproducible. Do not extend them by forking the Dockerfiles when the features system already covers what you need, and do not assume the MIT licence covers the images you pull, because the repository points generated images at a separate legal notice and a NOTICE.txt file.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What this repository is: published images, and a select set of them

The definition the README leads with is the one to keep in mind. A development container is a running Docker container with a well-defined tool and runtime stack and its prerequisites, used as a full-featured development environment, as a way to separate the tools, libraries and runtimes a codebase needs, and to aid continuous integration and testing.

This repository holds the images themselves, not the specification and not the tooling. Its own summary calls the contents a set of dev container images which are Docker images built with dev container features, and the only content directory it names is `src`, described as containing reusable dev container images. The published artefacts live under mcr.microsoft.com/devcontainers, and the project page is containers.dev.

What makes this repository unusual is a governance decision stated plainly in the README. It contains a select set of images, and the maintainers encourage the community to host and share additional images and features rather than adding them here, with a pointer to the distribution guidance in the specification repository and to the separate features repository for customisations you can adopt or modify. The reasoning is not written down, but the effect is clear: the official image set stays small enough to maintain and stays in step with the specification, while breadth is expected to come from features and from third-party images.

That decision has a direct consequence for an evaluator. If your language or toolchain is in the published set, this is the fastest path to a working environment. If it is not, the README's own answer is that you write your own image, which means this repository is the wrong dependency for your project and the interesting parts of the ecosystem are elsewhere.

How a devcontainer.json consumes these images, and where the spec stops

The relationship to the specification is worth being exact about, because the two are frequently confused.

The Development Containers Specification, in the README's description, seeks to enrich existing formats with common development-specific settings, tools and configuration while still providing a simplified, un-orchestrated single container option, so those formats can be used as coding environments or for continuous integration and testing. That specification lives in its own repository, and containers.dev is where the documentation sits. This repository supplies images that may be used in dev container configurations that follow the spec. Images and configurations are separate things, produced by separate projects.

The configuration file is where the two meet. A `devcontainer.json` is described as similar to `launch.json` for debugging, but designed to launch or attach to a development container instead, and at its simplest all you need is a `.devcontainer/devcontainer.json` file in your project that references an image, a `Dockerfile` or a `docker-compose.yml`, plus a few properties.

There is nothing to install from this repository. It publishes images, so the consuming step is writing that one file and letting your editor or CLI resolve the image reference. The un-orchestrated single container option in the spec is what makes the simplest configuration work: one container, no compose topology, no orchestrator, and the development-specific settings layered on top of whatever the image already provides.

For a team, the practical implication is that the image reference becomes part of your build inputs. Nothing in the README says images are immutable or that you should record a digest, but pinning a version tag is the difference between a reproducible environment and one that changes under you, and a registry image is a dependency like any other.

Why every RUN in these Dockerfiles chains commands with &&

The README has a question-and-answer section devoted to a single Dockerfile convention, which tells you how much thought went into the image builds. The question is why the Dockerfiles here use `RUN` statements with commands separated by `&&`.

The answer is layer mechanics. Each `RUN` statement creates a Docker image layer, and if a `RUN` statement adds temporary contents, those contents remain in that layer even if they are deleted in a subsequent `RUN`. The stated consequences are that the image takes more storage locally and that publishing it to a registry results in slower download times. The fix is to include the clean-up steps in the same `RUN` statement, chained with `&&`.

This is a small decision with a large effect on a registry-hosted image, and it explains why the project links to its own tips document on the subject. Package manager caches are the usual culprit: a download directory or a package index left behind in an earlier layer persists for every user who pulls the image, even though the final filesystem is clean. Chaining the removal onto the install is the difference between an image that is correct and one that is several hundred megabytes larger than it needs to be.

The broader point is that these images are published to a registry that people pull on every container creation, so the optimisation target is pull time rather than build time. That is a different set of trade-offs from a normal application image, and it explains other choices you will find if you read the Dockerfiles: fewer layers, no leftovers, and a preference for removing a tool in the same statement that installed it.

Images versus features: the division of labour in this ecosystem

The customisation story in this ecosystem has two halves, and confusing them is the most common mistake when reading the README.

Images are the base. They carry a language runtime, its package manager, and the prerequisites that runtime needs in order to be useful in a container. That is what this repository publishes, and it publishes a select set. Features are the add-on layer, maintained in a separate repository, and they are what let you put a specific tool, a specific version of a language server, or a specific CLI into an existing image without writing a Dockerfile. The README's contribution guidance is consistent with that split: if you want to add functionality on top of the available images, you use features, and if you want your own base, you write your own image, with Docker's own best-practices documentation and the features reference both linked as starting points.

The division has a real advantage over the older approach, which was to ask every image to bundle every tool. It means a tool that needs updating is fixed in one place, and an image that does not need a tool does not carry it. It also means the image set can stay small without limiting what a container can contain.

The cost is a second moving part. A devcontainer.json that references an image plus features now depends on two registries of things, and the feature versions are pinned in your configuration rather than baked into the image you pull. Anyone auditing a container environment has to resolve both, and the README does not document how feature versions are recorded or verified, so that resolution is on you.

Two licence layers: MIT for the repository, a separate notice for the images

The licensing here has two parts and the distinction matters more than it first appears.

The repository is MIT. The README states it plainly: copyright Microsoft Corporation, all rights reserved, licensed under the MIT License with the text in the `LICENSE` file. That covers the Dockerfiles, the build scripts and the documentation in this tree.

Then there is a second sentence that is easy to skim past. For images generated from this repository, the README points to a separate legal notice in the Microsoft container registry repository and to a `NOTICE.txt` file in this repository. So the artefact you actually pull from mcr.microsoft.com is governed by different terms from the source you read.

That is a normal arrangement for a registry of pre-built binaries, and it is not a red flag by itself. It does mean two things in practice. If your organisation runs a licence inventory, the images your developers pull are inputs that need to be recorded alongside your source dependencies, and the file to read is `NOTICE.txt` plus the registry notice rather than the `LICENSE`. And if you ever redistribute a container that incorporates one of these images, base or derived, you are redistributing Microsoft's image and its terms travel with it, which is a different conversation from redistributing MIT-licensed Dockerfiles.

This is a description of what the files say rather than legal advice. The practical advice is narrow: read `NOTICE.txt` before you build a distribution on top of an image, and record which image tag you used, because the terms of a specific image can be versioned independently of the repository you read them next to.

A Yarn 4 toolchain with handlebars templates and two very old pins

The repository's own build configuration is small and tells you how the images are produced. The manifest is five keys: a `files` array containing `src`, four dev dependencies, and a package manager pin.

The dependencies are the interesting part. `handlebars` is a templating engine, which is how one Dockerfile can be parameterised across many variants. `jsonc` is a JSON-with-comments parser, which is what you need to read `devcontainer.json` files and other annotated JSON during a build. `copyfiles` moves helper scripts into a build context, and `rimraf` and `mkdirp` are the cleanup and directory-creation helpers. The package manager is pinned to `[email protected]`, and the top level carries both `.yarn/` and `.yarnrc.yml`, so this is a modern Yarn setup rather than a legacy one.

Set against that, two of the pins are conspicuously old: `mkdirp` at 0.5.5 and `rimraf` at `^3.0.2`. Both are several major versions behind their current lines, in a repository that pushed on 2026-09-28. Neither is on the runtime path of a container, since they run at image build time, but they are still fetched and executed on every build, and a build-time dependency is a dependency. Whether that matters to you depends on your supply chain rules; what it does not do is affect the images, which are already built.

The rest of the top level explains the rest of the process. `build/` and `docs/` hold the build logic and the documentation that the README links to, `.azure-devops/` holds pipeline configuration consistent with publishing under a Microsoft registry, `cgmanifest.json` is a component governance manifest, which is how the components going into published images get inventoried, and `CODEOWNERS` records who owns what. The release tags, v0.4.31, v0.4.32 and v0.4.33 in the last fortnight, are build versions of the image set rather than a library's semantic version, so a consumer pinning a tag is pinning a set of images, not a package with a compatibility promise.

These images against your own Dockerfile and against a plain CI container

Three options, and the differences are about who carries the maintenance.

Building your own Dockerfile gives you total control and no dependency on Microsoft's opinion about which version of a runtime to ship. What you take on is the build step, which now runs on every developer machine and in CI, plus the layer hygiene problem the README describes, plus the fact that the toolchain in your image is one you have to track for security updates yourself. For a project with unusual requirements, or one that must pin an exact toolchain version for reproducibility, this is often the right answer, and the README says so.

Consuming a published image moves all of that to the publisher. Your environment starts immediately, the toolchain is curated and consistent across every developer, and the image has been built with the registry-pull constraints in mind. The costs are that you inherit defaults you did not choose, that changing anything means layering on top or using a feature, and that you are consuming a Microsoft-published artefact under its own terms rather than the MIT terms of the repository.

A plain CI container is the third option and the one most teams already have. A container image with your language and nothing else is enough to run tests, and it skips the entire devcontainer.json layer. What you give up is the point of the specification, which is the development-specific configuration: the settings that make an editor attach usefully to the container rather than merely running a shell inside it. If your only use is a headless test run, that layer buys you very little.

The honest summary is that this repository is a shortcut with a contract attached. It is an excellent shortcut when your stack is in the select set, and the wrong dependency the moment it is not, because the answer then becomes writing an image yourself, which is where this repository's own guidance points you.

Editorial conclusion

Use these images when your stack is in the published set and you want a development environment that starts without a build step, then pin a version tag and record the digest so your container is reproducible. Do not extend them by forking the Dockerfiles when the features system already covers what you need, and do not assume the MIT licence covers the images you pull, because the repository points generated images at a separate legal notice and a NOTICE.txt file. Verify first by adding a .devcontainer/devcontainer.json that references an image from mcr.microsoft.com/devcontainers, reading the image's documented properties on containers.dev, and checking the contents of NOTICE.txt before you redistribute a built environment.

Frequently asked questions

What is a dev container image in the devcontainers/images repository?

It is a pre-built Docker image published under mcr.microsoft.com/devcontainers for use as a development container, which the README defines as a running Docker container with a well-defined tool and runtime stack and its prerequisites. The images are built with dev container features, and the reusable image sources live in the src directory.

How do I use one of these images in a project?

Add a .devcontainer/devcontainer.json file in your project that references an image, a Dockerfile or a docker-compose.yml, plus a few properties. The README describes that file as playing the role launch.json plays for debugging, and points to containers.dev for the image list and their documented properties.

Why do the Dockerfiles in this repository chain commands with &&?

Because each RUN statement creates a layer, and temporary contents added by one RUN remain in that layer even when a later RUN deletes them. That makes the image larger locally and slower to download from a registry, so clean-up steps are chained onto the same RUN statement with &&. The README links to its own tips document on the subject.

Can I add my own image or tool to devcontainers/images?

The guidance is not to. The README says the repository contains a select set of images and encourages the community to host and share additional images and features instead, pointing at the distribution guidance in the specification repository and at the separate features repository for customisations to adopt or modify.

What licence covers the dev container images?

The repository itself is under the MIT License, copyright Microsoft Corporation, with the text in the LICENSE file. Images generated from the repository are pointed at a separate legal notice in the Microsoft container registry repository and at the NOTICE.txt file, so the published images carry their own terms.

How are the devcontainers images built and tracked?

The top level holds build and docs directories, pipeline configuration in .azure-devops, a component governance manifest in cgmanifest.json and a CODEOWNERS file. The repository's own tooling is a Yarn 4.9.4 setup with handlebars for templating, jsonc for annotated JSON, copyfiles, mkdirp at 0.5.5 and rimraf at ^3.0.2.

Official sources

  1. devcontainers/images on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/devcontainers-images.svg)](https://hysenlabs.com/projects/devcontainers-images)