How to Create a Dev Container for VS Code with devcontainer.json

Create a devcontainer.json file to define a Docker-based development environment that opens in VS Code with the exact tools, extensions, and settings your project needs.

7 min read

A dev container is a container configured as a development environment. You define it with devcontainer.json, then use the Dev Containers extension to build the container and reopen the project inside it.

The configuration can select an image, install named extensions, forward ports, and run setup commands. Only the tools and settings defined by the image and configuration are included.

What you need before starting

  • A supported container runtime. The standard local setup uses Docker Desktop on Windows or macOS, or Docker Engine on Linux. Remote Docker hosts are a separate supported workflow.
  • The Dev Containers extension. Install the Microsoft Dev Containers extension with ID ms-vscode-remote.remote-containers.
  • A trusted project. Review the dev container configuration, image, Features, Dockerfile, mounts, and lifecycle commands before building an unfamiliar repository.

Verify Docker is available before continuing:

bashbash
docker version

The command should report both client and server information. If it reports only the client or cannot reach the daemon, fix the Docker installation before building the container.

Step 1: Add a dev container configuration to your project

Open your project in VS Code. Open the Command Palette (F1) and run:

Dev Containers: Add Dev Container Configuration Files...

VS Code looks at the files in your project and suggests a relevant template. For a Node.js project, it suggests a Node template. For a Python project, a Python template. Pick one from the list or search for a different base.

After you select a template, VS Code may offer optional Features. Select only the tools the project needs and confirm the selection. VS Code creates the configuration and any supporting files required by the template.

Step 2: Understand the generated devcontainer.json

A minimal devcontainer.json using a pre-built image looks like this:

jsonjson
{
  "name": "Node.js Project",
  "image": "mcr.microsoft.com/devcontainers/typescript-node",
  "forwardPorts": [3000],
  "postCreateCommand": "npm install"
}

Here is what each property does:

PropertyPurpose
nameA label for the dev container configuration
imageThe Docker image to use. Microsoft publishes pre-built dev container images for Node.js, Python, Go, Rust, and others
forwardPortsPorts to expose from the container to your local machine
postCreateCommandA command that runs once after the container is created

If you chose a template with a Dockerfile instead, you see a build property referencing it. Use a Dockerfile when you need extra system packages beyond what the base image includes.

Step 3: Reopen your project in the container

After the devcontainer.json is created, VS Code shows a notification: Reopen in Container. Click it. You can also open the Command Palette and run Dev Containers: Reopen in Container.

VS Code builds the Docker image if needed, starts the container, installs the VS Code Server inside it, and reloads the window. A progress notification tracks each stage. The first build takes the longest. Later opens are fast because the image is cached.

When the Status Bar shows Dev Container: name, you are inside the container.

Step 4: Verify the container environment

Open a terminal (Terminal > New Terminal). The shell runs inside the container. Run a few checks:

bashbash
node --version
npm --version
pwd

The project files you see in the File Explorer are your local files, bind-mounted into the container. Edit a file locally and the change is instantly visible inside the container, and vice versa.

Step 5: Customize your devcontainer.json

Use the customizations.vscode.extensions property for exact extension IDs that should be installed in the container. Use Features for reusable tools, or a Dockerfile for persistent operating-system packages.

Container-specific settings belong under customizations.vscode.settings. Set a remote user only when that user exists in the image. Review lifecycle commands before running them because they execute project-controlled commands inside the container.

After editing devcontainer.json, rebuild the container to apply changes. Run Dev Containers: Rebuild Container from the Command Palette.

A rebuild recreates the container and removes software or files added manually inside its writable layer. Bind-mounted workspace files remain on the host, but you should commit work and move persistent setup into devcontainer.json, a Feature, or a Dockerfile before rebuilding.

Using a bind mount vs. a container volume

By default, VS Code bind-mounts your local project folder into the container. On macOS and Windows, bind-mount performance can be slower than native Linux.

For better performance, clone the repository directly into a Docker volume:

Start the volume workflow

Open the Command Palette and run Dev Containers: Clone Repository in Container Volume....

Choose the repository

Enter a Git URI, GitHub repository URL, branch URL, or pull request URL.

Wait for the container

VS Code clones the repository into an isolated Docker volume, builds the container, and connects to it.

This approach also keeps your local filesystem clean: no cloned folders, no node_modules on your host.

Troubleshooting

Docker build fails. Check the Dev Containers output log. Open the Command Palette and run Dev Containers: Show Container Log. The log shows the full Docker build output, including any errors.

Container starts but extensions do not install. Verify the extension IDs in devcontainer.json are correct. You can find an extension's ID by right-clicking it in the Extensions view and selecting Copy Extension ID.

File changes inside the container are slow. On macOS or Windows with a bind mount, large projects with many files can feel sluggish. Switch to cloning into a container volume, or add node_modules and build outputs to a Docker volume using the mounts property.

"Unable to write file" or permission errors. Check which user owns the mounted files and whether remoteUser matches a user that exists in the image. Reopen locally or use a recovery container before changing ownership, mounts, or Docker security settings.

Next, learn how to open a GitHub repository without cloning for quick browsing, or forward ports for local and remote services.

Rune AI

Rune AI

Key Insights

  • Create .devcontainer/devcontainer.json at the root of your project.
  • Use the "image" property for a pre-built image, or "build.dockerfile" for custom setups.
  • Add extensions under customizations.vscode.extensions to preinstall them in the container.
  • Use Dev Container Features to add tools like Git, GitHub CLI, or Docker without manual setup.
  • Run Dev Containers: Rebuild Container after editing devcontainer.json to apply changes.
RunePowered by Rune AI

Frequently Asked Questions

Where does the devcontainer.json file go?

Use .devcontainer/devcontainer.json or place .devcontainer.json at the project root. The .devcontainer folder is useful when the configuration also needs a Dockerfile or Compose file.

Do I need to write devcontainer.json from scratch?

No. Run Dev Containers: Add Dev Container Configuration Files... and choose a template and any required Features.

What is the difference between an image, a Dockerfile, and Docker Compose in devcontainer.json?

Use image for a prebuilt container image, build.dockerfile when you need a custom image, and dockerComposeFile when the development environment uses multiple services.

Conclusion

A committed devcontainer.json gives contributors a repeatable container definition. Keep the configuration small, rebuild after changing it, and put persistent tool installation in the image, Features, or lifecycle commands instead of making manual container changes.