Minimal, multi-arch Dev Container images: a small Debian base, plus one image per stack. Each carries only what its target stack needs, is built from a small set of verified upstreams, and ships with SLSA provenance, a GitHub artifact attestation, and an SBOM.
Pick the image for your stack from the Images table and reference it from
.devcontainer/devcontainer.json:
{
"image": "ghcr.io/bare-devcontainer/golang:1.27"
}The container runs as the non-root user dev and starts in /workspaces.
For anything beyond a trial, pin the digest as well — see Tags and pinning — and consider starting from the matching template in bare-devcontainer/templates, which adds security hardening (all Linux capabilities dropped, no-new-privileges) and volume mounts that persist the language and package manager caches across rebuilds.
A dev environment installs a lot of software from a lot of upstreams, which makes its supply chain hard to keep trustworthy. These images are built to keep that surface small and the contents auditable:
- Minimal, verified upstreams — each image carries only what its target stack needs, and only from the Debian package archive, Docker Official Images, and each upstream's own distribution channel, verified the way that upstream recommends such as GPG or minisign. What's not included is explicit about what that leaves out.
- Unprivileged and ready to use — every image runs as a non-root user that owns nothing outside its home directory and ships the Dev Container metadata a client needs to pick that user up. See The
devuser. - Verifiable builds — dependencies are pinned to versions and content digests, and every build publishes SLSA provenance, a GitHub artifact attestation, and an SBOM. See Verifying Published Images.
- Regular base updates — Renovate tracks the Debian base and each pinned upstream, so security patches are picked up promptly. Tags and pinning covers how a rebuild reaches a tag.
Microsoft publishes Dev Container base images too (devcontainers/images), but they carry packages many projects never use. These images are the minimal alternative: start from a small, auditable base and add exactly what the project needs.
These images are deliberately bare. Expect the following to be absent unless an image's own README says otherwise:
- No
sudo, and no root shell. Anything that needs root has to happen at build time in your ownDockerfile, or through a Dev Container Feature. - No development headers beyond libc. The base image ships
build-essential, so native extensions compile, but a library a project links against brings its own-devpackage. - No editors, shells, or CLI tooling beyond the basics.
bashandvim-tinyare present; editors, alternative shells, cloud CLIs, and linters are not. - No language toolchain in the version-manager images.
lean,mise,pnpm,rustup, anduvinstall the version the project declares rather than one baked into the image.
Add what a project needs with a Dev Container Feature, or
with your own Dockerfile built FROM one of these images.
Every image runs as dev (UID/GID 1000), starts in /workspaces, and declares remoteUser and containerUser through the devcontainer.metadata label, so a Dev Container client picks the user up on its own. dev owns its home directory and nothing else, so a client that remaps it to the host user's UID (updateRemoteUserUID, on by default on Linux) leaves no file behind under the old UID.
| Image | Registry | Use it for |
|---|---|---|
| bun | ghcr.io/bare-devcontainer/bun |
JavaScript/TypeScript with the Bun runtime |
| debian | ghcr.io/bare-devcontainer/debian |
The base for every other image; language-agnostic projects |
| deno | ghcr.io/bare-devcontainer/deno |
JavaScript/TypeScript with the Deno runtime |
| golang | ghcr.io/bare-devcontainer/golang |
Go, with the toolchain version pinned by tag |
| lean | ghcr.io/bare-devcontainer/lean |
Lean 4, with the toolchain chosen by the project |
| mise | ghcr.io/bare-devcontainer/mise |
Polyglot projects that pin their own runtimes |
| node | ghcr.io/bare-devcontainer/node |
Node.js, with Corepack instead of npm |
| opentofu | ghcr.io/bare-devcontainer/opentofu |
Infrastructure as code with OpenTofu |
| pnpm | ghcr.io/bare-devcontainer/pnpm |
Node.js, with the runtime version managed by pnpm |
| rustup | ghcr.io/bare-devcontainer/rustup |
Rust, with the toolchain chosen by the project |
| temurin | ghcr.io/bare-devcontainer/temurin |
Java, with the Eclipse Temurin JDK version pinned by tag |
| terraform | ghcr.io/bare-devcontainer/terraform |
Infrastructure as code with Terraform |
| uv | ghcr.io/bare-devcontainer/uv |
Python, with the interpreter managed by uv |
| zig | ghcr.io/bare-devcontainer/zig |
Zig, with the compiler version pinned by tag |
Every image is published for linux/amd64 and linux/arm64. See each image's README for its
available tags, the software it ships, and how its upstreams are verified.
The same builds are mirrored to Docker Hub as docker.io/baredevcontainer/<image>, with the
same tags and digests, so they stay available while ghcr.io is down. Prefer ghcr.io: it is
where each release lands first, and it has no
pull rate limits.
Each image publishes several tags per build. Using golang as an example:
| Tag | Points at | Moves when |
|---|---|---|
1.27.0-trixie |
An exact version on an exact Debian release | The image is rebuilt (base updates, security patches) |
1.27-trixie, 1-trixie |
The newest matching version on that Debian release | A new patch or minor version is published |
trixie |
The newest version on that Debian release | Any build of the image |
1.27.0, 1.27, 1 |
The same as the -trixie form; the default Debian release is implied |
Same as the -trixie form |
1.27.0-trixie-20260727 |
One specific build, by date | Never |
A tag is rebuilt when the definition behind it or the Debian base it is built on changes, and every tag is rebuilt at least once a week. Because the base image and its packages are refreshed on every build, every tag except the date-suffixed ones is mutable: the same tag resolves to different content over time. That is what makes security patches arrive automatically, and also why a tag alone is not a reproducible reference.
Tip
Pin the digest as well as the tag (image:tag@sha256:...). The tag stays readable, the
digest makes the reference exact, and Renovate or
Dependabot can raise a reviewable pull request whenever a new build is published. Find the
digest on the GitHub Container Registry
page, or with:
docker buildx imagetools inspect ghcr.io/bare-devcontainer/<image>:<tag>Each image has a matching template in bare-devcontainer/templates, which applies the recommended configuration out of the box: security hardening (dropping all Linux capabilities, no-new-privileges, running as a non-root user) and volume mounts that persist the language and package manager cache directories across rebuilds.
.devcontainer/devcontainer.json:
{
// Any image from the Images table, at the tag and digest you want.
"image": "ghcr.io/bare-devcontainer/debian:trixie@sha256:<digest>"
}.devcontainer/Dockerfile:
# Any image from the Images table, at the tag and digest you want.
FROM ghcr.io/bare-devcontainer/debian:trixie@sha256:<digest>.devcontainer/devcontainer.json:
{
"build": {
"dockerfile": "Dockerfile"
}
}.devcontainer/compose.yml:
services:
app:
# Any image from the Images table, at the tag and digest you want.
image: ghcr.io/bare-devcontainer/golang:1.27@sha256:<digest>
volumes:
- ..:/workspaces
command: sleep infinity.devcontainer/devcontainer.json:
{
"dockerComposeFile": "compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}"
}Each image is published with artifacts that let you confirm where it came from and audit what is in it.
The attestation is generated by actions/attest and verified with gh attestation verify:
gh attestation verify oci://ghcr.io/bare-devcontainer/<image>:<tag>@sha256:<digest> \
--owner bare-devcontainerThe Docker Hub mirror carries the same digests, so the same command verifies an image pulled from docker.io/baredevcontainer/<image>.
Docker Buildx embeds both in the OCI manifest: a SLSA provenance attestation describing how and where the image was built, and a Software Bill of Materials in SPDX format listing the packages it contains. Read either by naming it in --format:
docker buildx imagetools inspect ghcr.io/bare-devcontainer/<image>:<tag>@sha256:<digest> \
--format '{{json .Provenance}}'docker buildx imagetools inspect ghcr.io/bare-devcontainer/<image>:<tag>@sha256:<digest> \
--format '{{json .SBOM}}'To report a vulnerability, see SECURITY.md.
Bug reports, image requests, and pull requests are welcome. AGENTS.md describes how the repository is laid out and the conventions a change is expected to follow.