EXAMPLE 08
  • devcontainers
  • images

devcontainer Features

Add Node, Rust or the GitHub CLI to any image, in TOML.


The problem

Every repo needs a slightly different toolchain: this one Node 22, that one Rust, all of them gh. Maintaining a Dockerfile per repo for that is busywork, and the devcontainer ecosystem already packages each tool as a Feature.

devsandbox installs Features natively, without the devcontainer CLI: list them under features with the same references and options as in devcontainer.json.

Prompt for your coding agentPaste into Claude Code, Codex or any agent with a shell.

Look at my current devsandbox config and suggest devcontainer Features where they make sense.

  1. Read the devsandbox-cli and config-toml-spec skills first (load them if they are installed, otherwise read them from https://github.com/Nemikolh/devsandboxes/tree/main/skills). Follow them over your own assumptions: unknown config keys are hard errors.
  2. Find my devsandbox config dir: the folder holding devsandboxes.toml, passed to devsandbox as -C <config dir>. If you cannot tell which one, ask me.
  3. Find toolchains installed by Dockerfiles, lifecycle commands or heavy images that an official Feature could provide. List each suggestion and what it replaces, and apply only the ones I pick.
  4. Validate with devsandbox -C <config dir> ls and fix every error it reports before going on.
  5. Ask me before anything destructive: devsandbox rm, gc, rebuild --force, service rebuild, deleting files or branches.
fill in before sending Agent skills (opens in a new tab)

Config

devsandboxes.toml
[template.base]
image = "mcr.microsoft.com/devcontainers/base:ubuntu"   # plain Ubuntu, no toolchain
features = { "ghcr.io/devcontainers/features/github-cli:1" = true }   # gh everywhere, default options

[sandbox.web]
extends = "base"
folder = "../web"
features = { "ghcr.io/devcontainers/features/node:1" = "22" }   # a string sets the `version` option

[sandbox.api]
extends = "base"
folder = "../api"
features = { "ghcr.io/devcontainers/features/rust:1" = { profile = "minimal" } }   # or a table of options

Try it

TERMINAL
devsandbox run web                     # fetches the Features, builds the image
devsandbox run web --name fix-login    # its own worktree, same toolchain
devsandbox exec fix-login node --version   # v22.x
devsandbox exec fix-login gh --version
  • --name: the instance name.

Change a Feature, say node "20", and apply it:

TERMINAL
devsandbox rebuild fix-login           # drifted: recreated with node 20

How it works

  • Features are baked into an image derived from image or build, devsandbox-img-<sandbox>-feat; OCI references only (features).
  • features tables merge key by key under extends: web gets gh and node (templates).
  • = false skips a Feature, e.g. one inherited from a template.
  • Adding, removing or changing a Feature makes existing instances drift; devsandbox rebuild applies it (drift).

Source: site/src/content/examples/features.mdxEdit this page on GitHub(opens in a new tab)

MENU /
View on GitHub
EXAMPLES
All examplesEvery recipe on one page.Parallel coding agentsSeveral coding agents on one repo, fully isolated from one another.One shared PostgresOne development database for all your sandboxes and their instances.Private services per instanceEvery instance gets its own Postgres and Redis, so agents never touch each other's data.Shared package cachesNew sandboxes stop downloading the same packages again.ssh-agent and git pushAgents push over ssh with your host's keys, which never enter the container.Port forwarding on demandOpen a sandbox's dev server or database from your host, without restarting anything.Attach VS CodeOpen any instance in VS Code with one key.devcontainer FeaturesAdd Node, Rust or the GitHub CLI to any image, in TOML.Babysit pull requestsAn agent keeps your open PRs merged with their base, and pings you when it can't.Port a devcontainer.jsonTurn an existing devcontainer.json into a devsandbox config.