My company picked up a contractor in February to help with a backend project. He cloned the repo on a Monday, opened the folder in VS Code, and was running tests by lunch. He'd never seen our codebase. He didn't have to install Postgres, didn't have to figure out which Python version we were on, didn't have to debug a broken Homebrew. He hit "Reopen in Container" and went to work.
Five years ago that same task would have eaten his entire first day. The fix is dev containers, and I'm late to recommending them out loud, so let me do it now.
What a dev container actually is
A dev container is a Docker image that defines your project's development environment — language runtimes, system libraries, CLI tools, database clients, the whole stack. Your editor runs inside that container. The code is mounted from your machine; everything else lives in the container's filesystem.
The configuration is one JSON file at .devcontainer/devcontainer.json in the repo. Anyone who clones the repo gets the same environment, byte-for-byte. The container can be rebuilt cleanly. If your environment breaks, you trash it and create a new one in a few minutes.
VS Code has had first-class support for this for years. JetBrains IDEs added it more recently. There's an open spec backed by Microsoft, GitHub, and others, so it's not locked to one editor.
What it solved for us
Three things, in order of how much they hurt before:
First, the M-series Mac problem. Half our team is on Apple Silicon, the other half on x86 Linux. Some of our build tools didn't work cleanly on ARM. Dev containers run x86 emulation transparently when needed; the team stopped having to know which dependencies needed special handling.
Second, the version drift problem. We had three Python versions, two Node versions, and a Postgres install that was a year stale. New devs would brew upgrade something and break their environment in ways no one else could reproduce. Now every environment is identical and recreated from a Dockerfile checked into git.
Third, the "what was that command again" problem. Database setup, seed data, env files, mock services — all of it now runs as part of the container's startup. onCreateCommand runs once per container; postStartCommand runs every time the container starts. By the time the editor is open, the dev environment is fully ready.
The tradeoffs are real
Performance, on macOS especially. Docker on Mac runs Linux in a VM, and file I/O across that boundary is slow. Reading hundreds of files for a TypeScript build inside a dev container is noticeably slower than running the build natively. We mitigate this by putting node_modules on a Docker volume instead of the bind mount; that recovers most of the speed. But on a heavy project, you'll feel the overhead.
Editor lock-in is the other thing. If you're building dev containers around VS Code's spec, your team is implicitly committing to VS Code or compatible editors. We have one teammate who uses Neovim and works outside the container. Most teams I know just standardize.
Memory. A few containers running at once will eat through 16 GB. I bumped my work laptop to 32 GB partly because of this. Worth it; I wouldn't go back.
The minimal config that works
For a Node project, this is enough to get going:
{
"name": "my-project",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"postCreateCommand": "npm install",
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
}
}
}
That gives you Node 20, npm install on first launch, ESLint and Prettier extensions installed inside the container, and Docker-in-Docker so you can run other containers (a Postgres, a Redis) from inside. Six lines of meaningful config.
From there I usually add a docker-compose file for the supporting services and switch the dev container to dockerComposeFile mode. That's where it stops being trivial — multi-service setups are where the real complexity lives, but they're also where dev containers earn their keep most.
What I tell teams considering this
Don't roll it out as a mandate. Add a .devcontainer folder, tell the team it's there if they want it, and let it spread. The people who get burned by environment issues will adopt it first. Within a few months, when someone's struggling with a setup problem, the answer "have you tried opening it in a container" starts becoming the default.
Onboarding new contributors went from "block out a day" to "they should be productive within an hour." For a small team, that's hours saved every time someone joins. For an open-source project, it's the difference between contributors and not.
Five years late or not, I'm done writing READMEs that start with "first, install these eleven dependencies." That's what containers are for.