Local Development Environments: Dev Containers vs Nix vs Devbox vs Docker Compose
"It works on my machine" remains the most frustrating phrase in software development. Different operating systems, different tool versions, different system libraries, different configurations — the number of ways a development environment can differ between team members is enormous.
Tools for reproducible development environments aim to eliminate these inconsistencies. The goal: every developer gets an identical, working environment with one command, regardless of what operating system or machine they use.
Here is how the major approaches compare.
The Problem in Detail
A typical web application requires:
- A specific language runtime (Node.js 20.x, Python 3.12, Go 1.22)
- A database server (PostgreSQL 16, Redis 7)
- System libraries (libssl, libpq, imagemagick)
- Build tools (Make, CMake, protobuf compiler)
- Environment variables and configuration files
- Maybe a message queue (RabbitMQ, Kafka)
Setting this up manually on a new machine takes hours. Keeping it consistent across a team is nearly impossible. And onboarding a new developer becomes an exercise in debugging environment differences.
Dev Containers
Dev Containers is a specification for defining development environments using Docker containers. VS Code, JetBrains IDEs, and GitHub Codespaces support the specification natively. You define your environment in a devcontainer.json file, and the IDE creates and runs a container with your development environment.
How It Works
A .devcontainer/devcontainer.json file in your repository defines:
- The base container image (or a Dockerfile)
- VS Code extensions to install
- Port forwarding
- Environment variables
- Post-create setup commands
- Additional services (databases, caches) via Docker Compose
When you open the repository in VS Code, it offers to reopen in the container. Your code is mounted into the container, and all development happens inside it.
Strengths
- IDE integration: VS Code opens directly into the container with full IntelliSense, debugging, and extension support
- Reproducibility: Every developer gets the exact same environment, regardless of host OS
- GitHub Codespaces: The same devcontainer.json works in cloud-hosted Codespaces for zero-install development
- Docker ecosystem: Use any Docker image as a base, install anything with apt/yum/brew
- Multi-service: Include databases, caches, and other services via Docker Compose
- Portable: Works on macOS, Linux, and Windows (via WSL2)
Limitations
- Docker requirement: Developers need Docker Desktop (or an alternative like Podman) installed
- Performance on macOS: File system performance with mounted volumes on macOS can be slow for large codebases, though improvements have been made
- Memory overhead: Running development inside Docker consumes more memory than native development
- Non-VS Code editors: Support outside VS Code is less mature, though JetBrains Gateway and other tools are catching up
- Learning curve: Developers need some Docker knowledge to troubleshoot container issues
Best for: Teams using VS Code or GitHub Codespaces that want reproducible environments with strong IDE integration.
Nix
Nix is a package manager and build system that enables fully reproducible environments. A Nix shell defines every dependency your project needs, and Nix ensures those exact versions are available — regardless of what else is installed on the system.
How It Works
A flake.nix or shell.nix file in your repository declares all dependencies. Running nix develop (with flakes) or nix-shell drops you into a shell with exactly those dependencies available. Nothing more, nothing less.
Nix achieves reproducibility by building every package from source in an isolated environment and storing them at content-addressed paths (e.g., /nix/store/abc123-nodejs-20.11.0/). Different projects can use different versions of the same tool without conflicts.
Strengths
- True reproducibility: Nix pins every dependency to an exact version, including system libraries. "It works on my machine" becomes "it works on every machine"
- No containers: Nix runs natively on your machine. No Docker overhead, no file system performance issues
- Massive package repository: Nixpkgs has over 100,000 packages
- Composability: Combine dependencies from different sources without conflicts
- Cache sharing: Binary caches (cachix.org) avoid building everything from source
- Direnv integration: With
direnv, dependencies activate automatically when you enter the project directory
Limitations
- Steep learning curve: The Nix language is functional and unlike anything most developers have used. Configuration is powerful but opaque
- macOS rough edges: While Nix works on macOS, some packages are Linux-only, and macOS-specific issues arise periodically
- No services: Nix manages packages, not running services. You still need something else (Docker Compose, process-compose) for databases and other services
- Team adoption: Convincing a team to learn Nix is a harder sell than "install Docker and open VS Code"
- Disk space: The Nix store can grow large over time. Regular garbage collection is needed
Best for: Developers and teams willing to invest in the learning curve for maximum reproducibility and performance.
Devbox
Devbox builds on Nix but provides a dramatically simpler interface. According to Jetify, Devbox lets you create reproducible development environments using the Nix package manager without writing any Nix code.
How It Works
Initialize Devbox in your project (devbox init), add packages (devbox add nodejs@20 postgresql@16 redis), and start the environment (devbox shell). Devbox translates these commands into Nix configurations behind the scenes.
A devbox.json file tracks your dependencies, and devbox services can manage running processes like databases.
Strengths
- Nix power, simple interface: Access to the entire Nixpkgs repository without learning the Nix language
- No containers: Like Nix, Devbox runs natively with no Docker overhead
- Services:
devbox servicesmanages background services (PostgreSQL, Redis, etc.) with simple commands - Fast shell startup: Devbox caches aggressively, so entering the shell is quick after initial setup
- Scripts: Define project scripts in
devbox.jsonthat run in the Devbox environment - Cloud development: Devbox Cloud provides browser-based development environments
Limitations
- Nix installation required: Devbox needs Nix installed, which adds complexity on some systems
- Limited Nix customization: The simplified interface hides Nix's power. Complex requirements may need raw Nix
- Younger tool: Less proven at scale than Docker or Nix
- macOS considerations: Inherits Nix's macOS limitations
Best for: Teams that want Nix's reproducibility without Nix's learning curve.
Pricing: Free and open source. Devbox Cloud has a free tier.
Docker Compose
Docker Compose is not primarily a development environment tool — it is a multi-container orchestration tool. But many teams use it to define their development dependencies (databases, caches, message queues) alongside their application.
How It Works
A docker-compose.yml file defines services that your application needs. docker-compose up starts them all. Your application code runs on your host machine (or in another container) and connects to the Compose-managed services.
Strengths
- Ubiquitous: Most developers already know Docker Compose
- Service management: Excellent for managing databases, caches, and other services
- Port mapping: Expose services on localhost ports for easy application connectivity
- Volume mounting: Persistent data survives container restarts
- Profiles: Run different service combinations for different scenarios
- Platform independent: Works on macOS, Linux, and Windows
Limitations
- Not an environment tool: Docker Compose manages services, not your development toolchain. You still need Node.js, Python, etc. installed on your host
- No tool versioning: Docker Compose does not solve the "wrong Node.js version" problem
- Resource consumption: Running multiple containers for databases and services consumes memory
- Networking complexity: Container networking adds a layer of indirection that can confuse debugging
Best for: Teams that need service dependencies (databases, caches) but want to run application code natively.
Comparison
| Feature | Dev Containers | Nix | Devbox | Docker Compose | |---------|---------------|-----|--------|----------------| | Tool versioning | Yes | Yes | Yes | No | | Service management | Yes (via Compose) | No (external) | Yes | Yes | | Container required | Yes | No | No | Yes (services only) | | Learning curve | Low-Medium | High | Low | Low | | IDE integration | Excellent | Limited | Limited | N/A | | Performance | Medium | High | High | N/A | | Cloud option | GitHub Codespaces | No | Devbox Cloud | No |
Practical Recommendations
For VS Code Teams
Dev Containers is the path of least resistance. The IDE integration is seamless, the configuration is straightforward, and GitHub Codespaces provides a cloud fallback.
For Performance-Sensitive Development
Devbox (or Nix for teams willing to invest) avoids the container overhead. File system operations run at native speed, which matters for large codebases with many files.
For Polyglot Teams
Devbox or Nix handles multiple language runtimes cleanly. Need Node.js 20 and Python 3.12 and Go 1.22 and Rust 1.78? Add them all without version conflicts.
For Service-Heavy Applications
Docker Compose remains the best option for running databases, message queues, and other services locally. Combine it with Devbox or mise for tool version management.
The Hybrid Approach
Many teams combine tools:
- Devbox for language runtimes and CLI tools
- Docker Compose for databases and services
- direnv for automatic environment activation
This gives you native performance for development tools, containerized services for infrastructure, and automatic activation when you enter the project directory.
The goal is not picking the "best" tool — it is ensuring every developer on your team can go from git clone to running the application with minimal friction. Pick the approach that your team will actually use, and document it in your repository's README.