Jiwon Min Developer

A Guide to Building the Perfect Development Environment with Docker and VS Code Dev Containers

“But it works on my machine!” This is one of the most common and frustrating problems in collaborative development. Subtle differences in operating systems, installed library versions, and various environment variable settings among developers can lead to unpredictable bugs and derail entire projects. This issue is known as “environment inconsistency.”

The solution to this problem is container technology, with Docker at its core. Docker packages an application and all its dependencies into an isolated “container,” ensuring it runs identically in any environment. This eliminates the pain caused by differences between development and production environments.

Taking this a step further, Visual Studio Code’s Dev Container feature transforms a Docker container from a simple execution environment into a complete “development environment.” By connecting the VS Code editor itself inside the container, you can write code, debug, and use the terminal as conveniently as if you were working in your local environment. This post will provide a detailed, step-by-step guide on how to use Docker and VS Code Dev Containers to build a consistent and reproducible development environment for anyone, on any OS.

A Guide to Building the Perfect Development Environment with Docker and VS Code Dev Containers

© AI Generated by Imagen 4.0


Why is Development Environment Isolation Important?

Imagine a new team member joins your project. They need to install the necessary language runtimes (like Python or Node.js), databases, and various libraries on their laptop. During this process, the following problems can arise:

  • Operating System (OS) Differences: Windows, macOS, and Linux users experience slight variations in installation processes, commands, and file paths.
  • Dependency Hell: Version conflicts can occur with globally installed libraries. For example, Project A might require Python 3.8, while Project B requires 3.10.
  • The ‘Swamp of Configuration’: Manually configuring numerous environment variables and settings files is time-consuming and prone to errors.

These issues ultimately lead to the “it works on my machine, but not on yours” situation, resulting in wasted debugging time and a decrease in the entire team’s productivity. Isolating and standardizing the development environment is the essential first step to fundamentally solving these problems.

Docker Containers: The Start of Consistency

Docker packages everything an application needs to run (code, runtime, system tools, libraries, etc.) into a package called an Image. When this image is run, it becomes an isolated process called a Container.

The biggest advantage is Portability. Anywhere Docker is installed, you can run the same container from the same image. This guarantees an identical environment on a developer’s laptop, a test server, and the actual production server. The required environment is defined as code in a simple text file called a Dockerfile, making the environment configuration itself transparent, reproducible, and subject to version control.

VS Code Dev Container: A Revolution in Development Experience

While Docker provides an excellent execution environment, the development process itself still takes place on the developer’s local machine. The source code resides locally, and the editor runs locally. VS Code’s Dev Container (Development Container) extension breaks down these boundaries.

With Dev Containers, VS Code connects inside a specified Docker container, using the container itself as a full-featured development environment.

  • Code Completion, Linting, and Debugging: All of VS Code’s powerful features work directly inside the container.
  • Terminal: When you open the integrated terminal in VS Code, you get direct access to the container’s shell, not the host machine’s.
  • Extensions: You can also install and run necessary VS Code extensions in isolation within the container.

In short, a developer no longer needs to install any project-related tools or libraries on their own PC. With just Docker and VS Code, all development can be completed inside a container.

Hands-On! Building a Dev Container Environment

Let’s now build a Dev Container environment for a simple Node.js project.

Prerequisites

First, you need to have the following tools installed:

  1. Docker Desktop: Install the version appropriate for your OS. It provides a GUI for easily managing the Docker engine. (Download Docker Desktop)
  2. Visual Studio Code: Install the latest version of VS Code. (Download VS Code)
  3. Dev Containers extension: Search for ms-vscode-remote.remote-containers in the VS Code Marketplace and install it.

Step 1: Create a Project Folder and Launch VS Code

First, create an empty folder for your project and open it with VS Code.

mkdir my-dev-container-project
cd my-dev-container-project
code .

Step 2: Add Dev Container Configuration Files

With VS Code open, press F1 or Ctrl+Shift+P (macOS: Cmd+Shift+P) to open the Command Palette. Then, type and select Dev Containers: Add Dev Container Configuration Files....

A list of templates for various languages and frameworks will appear. For this example, we’ll select Node.js & TypeScript. When prompted to select a version, choose your desired version (e.g., 18). When asked to select additional features to install, you can just click OK for now.

Step 3: Analyze the Generated Files (the .devcontainer folder)

After completing the previous step, a .devcontainer folder will be created in your project root, containing a devcontainer.json file and a Dockerfile.

devcontainer.json

This is the core configuration file for the Dev Container.

// .devcontainer/devcontainer.json
{
	"name": "Node.js & TypeScript",
	// Configure to build an image using a Dockerfile.
	"build": {
		"dockerfile": "Dockerfile"
	},

	// VS Code-related customizations
	"customizations": {
		"vscode": {
			"settings": {},
			// List of VS Code extensions to install in the container.
			"extensions": [
				"dbaeumer.vscode-eslint"
			]
		}
	},

	// Command to run after the container is created.
	// "postCreateCommand": "npm install",

	// Port forwarding settings between the host and container.
	"forwardPorts": [3000],

	// The user to connect as in the container.
	"remoteUser": "node"
}
  • build.dockerfile: Specifies which Dockerfile to use to build the environment.
  • customizations.vscode.extensions: A list of VS Code extension IDs to automatically install and enable in this container environment.
  • forwardPorts: Maps a port inside the container to a port on your local machine. This is useful for web server development.
  • postCreateCommand: Allows you to specify a shell command to be executed automatically after the container is first created (e.g., npm install).

Dockerfile

This file is a script that defines how to build the Docker image.

# .devcontainer/Dockerfile
# Start from the official Node.js image.
FROM mcr.microsoft.com/devcontainers/typescript-node:0-18

# [Optional] You can add further setup, like installing system packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
#     && apt-get -y install --no-install-recommends <your-package-list-here>

This Dockerfile is based on a pre-built image for Node.js development provided by Microsoft. If needed, you can add commands like apt-get to install additional system libraries.

Step 4: Reopen the Project in the Container

Once the configuration files are created, a notification toast will appear in the bottom-right corner of VS Code saying “Reopen in Container”. Click this button.

If you miss the notification, you can press F1 to open the Command Palette and run Dev Containers: Reopen in Container.

VS Code will now automatically perform the following tasks: build a Docker image based on the Dockerfile, run a container from that image, and then install and connect the VS Code backend server inside the container. This process may take a few minutes the first time it runs.

Step 5: Start Development and Verify the Environment

When everything is finished, the VS Code window will reopen. You are now ready to develop inside the container.

Open the integrated terminal in VS Code (Ctrl+ ). Then, check the Node.js version.

node -v
# Output: v18.x.x (the version specified in the Dockerfile)

From now on, all commands you run in this terminal (npm install, npm start, etc.) will execute inside the completely isolated Docker container, not on your host machine. Now, any team member can clone this project from Git, click “Reopen in Container,” and have an environment that is 100% identical to yours in just a few minutes.

References