Getting Started With Dev Containers
Getting started with Dev Containers is pretty straightforward. To use them, you'll need:
A code editor that supports Dev Containers. Dev Containers currently work with Visual Studio Code and JetBrains IDEs like WebStorm. For this guide, we'll focus on VS Code.
Docker Desktop installed. Dev Containers use Docker to build and run containers. So you'll need Docker Desktop for your OS installed and running.
A devcontainer.json file: This file defines your container environment. It specifies things like:
- The Docker image you want to use (e.g., node:12-alpine)
- Folders to mount into the container
- Environment variables
- Post-create scripts to run
VS Code has snippets to help you generate a devcontainer.json file for popular tech stacks.
Step-by-Step Example:
Create a new project folder:
mkdir my-dev-container-project
cd my-dev-container-projectOpen the project in VS Code:
code .Add a .devcontainer folder with a devcontainer.json file:
mkdir .devcontainer
touch .devcontainer/devcontainer.jsonEdit the devcontainer.json file:
{
"name": "Node.js Sample",
"dockerFile": "Dockerfile",
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"extensions": [
"dbaeumer.vscode-eslint"
],
"postCreateCommand": "npm install",
"remoteUser": "node"
}Add a Dockerfile to the .devcontainer folder:
FROM node:12-alpineBuild and run the container:
- Press
F1and select "Remote-Containers: Reopen in Container" to build the container image and reopen the folder in the container. - VS Code will build the container, install its dependencies, and start the container - all in the background.
- Your VS Code window will reload, and you'll be working directly in the container with everything set up and ready to go!
Anytime you need to build a fresh instance of the container, just run the "Remote-Containers: Rebuild and Reopen in Container" command again.
Choosing a Base Image
Choosing a base image for your dev container is an important first step. This base image contains the basic Linux operating system and initial tools/settings that your container will build upon.
Language-Specific Images If you're building a container for a particular programming language like Python (opens in a new tab), JavaScript, or Go, start with an image tailored for that language.
These images will have the runtime and base packages pre-installed so you can get started coding right away. Some popular options include:
- python: For Python development. Comes with Python, Pip, and other basics.
- node: For JavaScript/Node.js projects. Includes Node.js and NPM.
- golang: For Go development. Comes with the Go compiler, build tools, and common libraries.
General Purpose Images
For a more flexible dev container base, you can choose a general purpose image like:
- Ubuntu (opens in a new tab): A popular Linux distribution with a lightweight footprint. Easy to install any languages/tools on top.
- Debian (opens in a new tab): Another popular, open-source Linux OS. Stable and reliable.
- Alpine (opens in a new tab): A tiny Linux distribution perfect for containers. Only a 5MB image but you can still install whatever you need.
These general images give you more control to fully customize your container's contents. However, it also means more work upfront to get your programming environment set up. It depends if you prefer convenience or flexibility!
In the end, choose an image that has:
- The minimum tools/packages you need to get started
- A small footprint for fast builds and load times
- Active maintenance to keep the image secure and up-to-date
Your dev container's base image establishes a solid foundation. From there, you can install specific tools, sync in source code, and fully configure your development environment. The possibilities are endless!
Adding Tools and Runtimes
Adding tools and runtimes to your dev container gives you a lot of flexibility. You have a few options for how to do this:
Install Tools/Runtimes When You Build the Image
This is good if you know exactly what you need for your project ahead of time. You can install tools/runtimes using the Dockerfile with RUN apt-get install.
# Dockerfile
FROM node:14
# Install Python
RUN apt-get update && apt-get install -y python3 python3-pipInstall Tools/Runtimes When You Start the Container
This is useful if you want to install things at runtime or if your tooling needs change from project to project. You can install tools/runtimes using the devcontainer.json postCreateCommand.
{
"name": "Node.js Sample",
"image": "mcr.microsoft.com/vscode/devcontainers/javascript-node:0-14",
"postCreateCommand": "npm install && pip install requests"
}Use devcontainer.json build.args to Pass in Tools/Runtimes as Build Arguments
This lets you build once but start the container multiple times with different tools and runtimes. You pass the build args when you start the container, and the Dockerfile uses the build args to conditionally install tools/runtimes.
# Dockerfile
ARG PYTHON_VERSION=3.8
FROM python:${PYTHON_VERSION}
RUN apt-get update && apt-get install -y curl{
"name": "Python Sample",
"build": {
"dockerfile": "Dockerfile",
"args": {
"PYTHON_VERSION": "3.9"
}
}
}You can refer to this documentation for understanding the syntax and usage of build arguments: Docker ARG documentation (opens in a new tab)
Create a devcontainer.json with Multiple Builds for Different Stacks
This allows you to pick a "stack" when starting the container and get a set of predetermined tools. You define multiple build objects in devcontainer.json, each with a Dockerfile to install a particular set of tools. The devcontainer.json (opens in a new tab) reference guide explains the available options and settings you can use to customize your development containers.
Install a Common Set of Tools in the Dockerfile, Then Use Install-Specific Tools in devcontainer.json
This gives you a base set of tools on start, then you can install additional tooling as needed for your particular project in the postCreateCommand.
Use Docker Compose to Start Additional "Sidecar" Containers with Other Tools/Runtimes
This lets you start up other containers with tooling/runtimes that your main dev container can access. You define the sidecar services in a docker-compose.yml file referenced by devcontainer.json. The Docker Compose documentation (opens in a new tab) explains how to define and manage multi-container applications using Docker Compose YAML files.
# docker-compose.yml
version: '3'
services:
dev:
build: .
volumes:
- .:/workspace
command: npm start
db:
image: postgres
environment:
POSTGRES_PASSWORD: exampleUsing a combination of these techniques, you can craft a dev container with a robust set of tools for any development needs.
The options are plentiful - you just have to choose what works best for your particular project!