devcontainer.json for your repository — from a minimal config to a full-stack setup with Docker Compose, port forwarding, and GitHub Codespaces support.
Quick Start
Create.devcontainer/devcontainer.json in your repository root:
Choosing Your Base
Image Only (Simplest)
Use a pre-built devcontainer image when you don’t need custom tooling:javascript-node, python, go, rust, java, dotnet, universal. See the full list.
Dockerfile (Recommended for Most Projects)
Use a Dockerfile when you need additional tools (package managers, CLIs, databases):.devcontainer/Dockerfile:
Why Dockerfile over features? Devcontainer features (
"features": { ... }) work well locally but can cause issues in GitHub Codespaces — SSH may connect to the wrong container layer. Baking everything into a Dockerfile is more reliable.Port Configuration
Declaring Ports
List all ports your application uses inforwardPorts. Commander reads this to set up port forwarding:
Adding Labels
UseportsAttributes so Commander’s UI shows meaningful service names instead of just port numbers:
Multiple Workspaces (Port Conflicts)
When running multiple workspaces for the same repo, ports will conflict. Enable Offset Ports on Conflict in Commander settings (on by default) — Commander will automatically find available ports (e.g., 3001 instead of 3000 for the second workspace). For projects with their own multi-instance port scheme (like instance-indexed ports), declare the actual ports:Environment Variables
Inside the Container
UsecontainerEnv to set environment variables inside the running container:
Codespace-Specific Variables
UseremoteEnv for variables that should only be set in GitHub Codespaces:
Commander-Side Variables
Commander also supports Additional Env Vars in its environment settings (Settings → Environments). These are merged withcontainerEnv at runtime — useful for secrets you don’t want in version control.
Lifecycle Commands
Commander executes these hooks at specific points in the container lifecycle. All commands run inside the container at the workspace folder.initializeCommand
Runs on the host machine (not inside the container) before the container is created. Use for host-side setup:postCreateCommand
Runs once after the container is first created. Use for installing dependencies:postStartCommand
Runs every time the container starts (including restarts). Use for starting services:Tip: The
postStartCommand should be idempotent — safe to run multiple times. If it starts Docker containers, make sure it handles the case where they’re already running.postAttachCommand
Runs each time Commander attaches to the container (e.g., after reconnection). Rarely needed.Multi-Command Syntax
For multiple setup steps, use the object form — each command runs in parallel:Execution Order
Docker-in-Docker Support
If your project runs Docker containers (e.g., PostgreSQL, Redis via Docker Compose), you need Docker available inside the devcontainer.Setup
- Install Docker CLI in your Dockerfile:
- Mount the Docker socket in
devcontainer.json:
Codespaces: Docker Volume Mounts
In GitHub Codespaces, Docker runs on the host VM, not inside the devcontainer. This means Docker volume mounts use the host’s filesystem, not the devcontainer’s. Yourdocker-compose.yml volumes need to use the host path:
PROJECT_ROOT:
GitHub Codespaces Requirements
For Codespace environments to work:Required
- sshd feature — Commander uses
gh codespace sshto run commands:
- GitHub account connected in Commander (Settings → Integrations)
- Codespaces enabled on the repository or organization
Recommended
ghCLI installed locally — needed forenvironment_execandsync_filesagent tools- 4-core machine or larger for monorepos with Docker infrastructure (2-core may run out of memory)
Machine Sizing Guide
| Machine | RAM | Best For |
|---|---|---|
| 2-core | 8 GB | Simple projects, no Docker infrastructure |
| 4-core | 16 GB | Medium projects with a database or two |
| 8-core | 32 GB | Monorepos with Docker Compose stacks |
| 16-core | 64 GB | Large monorepos with build-heavy workflows |
Multiple Configurations
For monorepos or projects with different environment needs, define multiple configs using named subdirectories:Examples
Simple Node.js API
A minimal setup with no Docker infrastructure:Python + PostgreSQL
A Dockerfile-based setup with Docker Compose infrastructure:.devcontainer/devcontainer.json
.devcontainer/Dockerfile
Full-Stack TypeScript Monorepo
Here’s the full devcontainer setup used by the Cyshel Platform — a TypeScript monorepo with Docker Compose infrastructure (PostgreSQL, Redis, Restate, LocalStack):.devcontainer/devcontainer.json
.devcontainer/Dockerfile
Commander Settings
After adding the devcontainer to your repo:Select config (if multiple)
If your repo has multiple devcontainer configs, pick which one to use from the dropdown.
Enable auto-provision (optional)
Turn on Auto-Provision so new workspaces automatically get an environment.
Settings Reference
These settings are configured per-repository in Commander (not in devcontainer.json):| Setting | Description | Default |
|---|---|---|
| Environment Mode | None, Local Docker, or GitHub Codespace | None |
| Auto-Provision | Automatically provision for new workspaces | Off |
| Devcontainer Config | Which config file to use | Auto-detected |
| Preserve Volumes | Keep Docker volumes across rebuilds | On |
| Offset Ports on Conflict | Auto-assign ports when conflicts occur | On |
| CPU Limit (Local) | Max CPU cores for the container | Unlimited |
| Memory Limit (Local) | Max RAM for the container | Unlimited |
| Machine Type (Codespace) | GitHub Codespaces machine size | 2-core |
| Idle Timeout (Codespace) | Minutes before auto-suspend | 30 |
| Additional Env Vars | Extra variables (for secrets, overrides) | Empty |
| Additional Ports | Extra ports beyond devcontainer.json | Empty |
| Additional Mounts | Extra volume mounts | Empty |
| Post-Provision Scripts | Scripts to run after provisioning | Empty |
Troubleshooting
“No devcontainer config found” Commander didn’t detect your config. Check that the file is at.devcontainer/devcontainer.json (not .devcontainers/), the JSON is valid (JSONC with comments is fine), and the file is committed and present in the workspace’s worktree.
“No projects found in /app”
Your Docker Compose volumes can’t access the workspace files. Set PROJECT_ROOT to the Docker host path — see Docker Volume Mounts above.
“Cannot find module” in postStartCommand
The command runs from the workspace folder, but your working directory might not be what you expect. Use absolute paths or explicitly cd /workspaces/my-project first.
Port forwarding exits immediately
Services aren’t listening yet. Commander retries port forwarding automatically up to 5 times. Ensure your postStartCommand actually starts the services.
Container build fails
Check your Dockerfile builds outside Commander: docker build -t test .devcontainer/. If using cacheFrom, ensure the cache image is accessible. Large images may time out — use multi-stage builds.
Codespace shows Alpine instead of your image
Devcontainer features didn’t install correctly. Use a Dockerfile instead of "image" + "features". See Choosing Your Base.
SSH connects to wrong container
Add the sshd feature to your devcontainer.json — see Codespaces Requirements.
Docker commands fail inside container
Verify the Docker socket mount is in your config. Check the container user is in the docker group. In Codespaces, Commander automatically fixes Docker socket permissions.
2-core codespace runs out of memory
Your project is too large for the smallest machine. Increase to 4-core or 8-core in Settings → Environments → Machine Type.
Environment stuck in “provisioning” or “building”
Check Docker is running (for local mode) and internet connectivity (for image pulls). Try destroying the environment and re-provisioning via the environment panel.
For more details on the environment UI and agent tools, see Environments.