The problem
Your repo already has a .devcontainer/devcontainer.json and you want to run it with devsandbox. Rewriting it from scratch risks losing something.
devsandboxes.toml uses the same property names in TOML, so most of the file moves over line by line. The differences: folder says which repo the sandbox is for, some properties are ignored, and compose setups become [services.*].
Config
{
"name": "web",
"image": "mcr.microsoft.com/devcontainers/typescript-node:22",
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"forwardPorts": [3000],
"containerEnv": { "NODE_ENV": "development" },
"mounts": ["source=${localEnv:HOME}/.config/gh,target=/home/node/.config/gh,type=bind"],
"remoteUser": "node",
"postCreateCommand": "npm ci",
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint"],
"settings": { "editor.formatOnSave": true }
}
}
}[sandbox.web] # the table key replaces `name`
folder = "../web" # the repo; devsandboxes.toml lives outside it
image = "mcr.microsoft.com/devcontainers/typescript-node:22"
forwardPorts = [3000] # forwarded while the instance runs
containerEnv = { NODE_ENV = "development" }
mounts = ["source=${localEnv:HOME}/.config/gh,target=/home/node/.config/gh,type=bind"]
remoteUser = "node"
postCreateCommand = "npm ci"
# settings: ignored, keep them in the repo's .vscode/settings.json
customizations.vscode.extensions = ["dbaeumer.vscode-eslint"]
[sandbox.web.features]
"ghcr.io/devcontainers/features/github-cli:1" = {}Try it
From the config dir:
devsandbox ls # validates the config, no container needed
devsandbox run web
devsandbox exec -it web zshHow it works
The devcontainer.json can stay in the repo; devsandbox never reads it.
Property mapping
| devcontainer.json | devsandboxes.toml | notes |
|---|---|---|
| the file’s location | folder |
required by run |
name |
the table key | name is ignored |
image, features |
same | Features: OCI references only |
build.* |
build = { dockerfile, context, … } |
paths relative to the config dir |
containerEnv, remoteEnv |
same | containerEnv not ${…}-substituted |
containerUser, remoteUser |
same | |
mounts |
same | missing bind sources are created |
workspaceFolder |
same | default /workspaces/<folder basename> |
workspaceMount |
folder |
ignored |
init, privileged |
same | |
initializeCommand |
same | runs on the host, in the config dir |
onCreateCommand … postAttachCommand |
same | tables run in order, not in parallel |
customizations.vscode.extensions |
same | |
customizations.vscode.settings |
repo .vscode/settings.json |
ignored |
forwardPorts |
same | forwarded by the host daemon while the instance runs |
appPort, portsAttributes |
devsandbox port web 3000 |
ignored |
runArgs, capAdd, securityOpt |
none | ignored |
dockerComposeFile, service, runServices |
[services.*] + services |
error |
Implemented, ignored, rejected
ls errors on unknown keys; run warns about ignored ones.
- Implemented:
image,build,features,workspaceFolder,mounts,containerEnv,remoteEnv,containerUser,remoteUser,init,privileged, the six lifecycle commands,customizations.vscode.extensions,forwardPorts(details). - Ignored:
name,appPort,portsAttributes,otherPortsAttributes,runArgs,workspaceMount,overrideFeatureInstallOrder,updateRemoteUserUID,userEnvProbe,overrideCommand,shutdownAction,capAdd,securityOpt,hostRequirements,waitFor,secrets, othercustomizations.*(details). - Rejected:
dockerComposeFile,service,runServices, and any unknown key. - Variables: as in devcontainers, plus
${configDir},${sharedVolumes},${instance}; substituted only in some keys (variables).
Coming from docker compose
The app container is the sandbox; each other compose service becomes a [services.<name>] (image, env, ports, command) that the sandbox lists in services and reaches by name as hostname. See private services per instance and one shared Postgres.
Agent instructions
Port the devcontainer.json of my repo at <repo path> to a sandbox in my devsandbox devsandboxes.toml.
- 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.
- 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. - Read <repo path>/.devcontainer/devcontainer.json and any Dockerfile or docker-compose.yml it references; do not modify them.
- Add a
[sandbox.<sandbox name>]withfolderpointing at <repo path> (relative to the config dir) and copy the implemented properties over; rebasebuildpaths onto the config dir. Extend an existing template instead of repeating what it provides. - Drop the properties the config-toml-spec lists as ignored and tell me what each did; turn compose services into
[services.*]listed inservices. - Validate with
devsandbox -C <config dir> lsand fix every error it reports before going on. - Run
devsandbox -C <config dir> run <sandbox name>and report anywarning:lines, thendevsandbox -C <config dir> exec <instance> <a command the repo needs>. - Ask me before anything destructive: devsandbox rm, gc, rebuild --force, service rebuild, deleting files or branches.
Source: site/src/content/examples/port-a-devcontainer.mdxEdit this page on GitHub(opens in a new tab)