What are Dev Containers?

Image source: makeameme.org

Sounds familiar? You are not alone. Consistency between development, testing and production environments has been the bane of software developers for a long time. Differences in runtime versions, OS library and package dependencies, tools and projects can easily lead to configuration issues, lost productivity, and debugging nightmares. What if you could simply "ship your dev machine"? Well, that's what the Development Containers initiative is all about. Let's dive into it.

Development Containers (or dev containers for short) allow you to create Docker containers that build the exact development environment you state, including the frameworks, tools and libraries required for your application, and run it inside the container. This approach makes it easy to onboard new developers, replicate and troubleshoot bugs, eliminate inconsistencies, and improve your security posture. The Development Containers Specification looks to enrich existing formats with metadata for common development-specific settings, tools, and configuration. devcontainer.json, the first format in the specification, is a structured JSON with Comments (jsonc) metadata format used to store the necessary configuration.

Image source: containers.dev/overview

Here is a sample devcontainer.json template for Alpine; see additional templates here, and the complete specification here. The configurations are usually image, Dockerfile, or Docker Compose-based, and can include features, environment variables, mounts, users, and other sections.

{
	"name": "Alpine",
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
	"image": "mcr.microsoft.com/devcontainers/base:alpine-${templateOption:imageVariant}"

	// Features to add to the dev container. More info: https://containers.dev/features.
	// "features": {},

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],

	// Use 'postCreateCommand' to run commands after the container is created.
	// "postCreateCommand": "uname -a",

	// Configure tool-specific properties.
	// "customizations": {},

	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
	// "remoteUser": "root"
}

Alright, so you are sold on dev containers - how do you get started?

  • In the root folder of your Git repo, create a subfolder called .devcontainer. Inside the subfolder, create a file called devcontainer.json.
  • Build your desired environment from scratch, or start from a predefined template, and add libraries as needed. Add dev container features (or tools) as necessary, using the features section of the JSON file.
  • To build your environment from scratch instead, create a Dockerfile, store it in the .devcontainer subfolder, and reference it in devcontainer.json, which will now look like:
{
    "name": "Docker",
    "build": {
       "dockerfile": "Dockerfile"
    }
}
  • If your application runs in a container too, your can move the Dockerfile to the project root, and use a single Dockerfile to handle your release too.
  • Run the dev container using a supported IDE e.g. Visual Studio Code, GitHub Codespaces, Cachix Devenv, Jetpack Devbox, DevPod, and others.
  • Profit!