In 2026 “works on my machine” is finally curable. Four tools — Nix flakes, Devbox, mise, and devcontainers — let you ship a git clone && cd project && one-command-setup experience that actually works on every team member’s laptop. This post is the comparison and a recommendation by team shape.
The problem
Onboarding a new engineer:
- Install Python 3.13 (oh wait, 3.12).
- Install uv (or poetry, or conda?).
- Install Postgres 18 (oh, you have 16).
brew installten things.- Hope nothing’s pinned to a different version.
- Hope the OS isn’t too different.
A reproducible dev environment makes “I have the project running locally” the first 5 minutes after git clone, not the first day.
The four contenders
| What it pins | Reproducibility | Curve | Best for | |
|---|---|---|---|---|
| Nix flakes | Everything (libs, compilers, system utils) | Highest | Steep | Complex monorepos, teams willing to learn |
| Devbox | Same as Nix, friendlier | Very high | Moderate | Most teams that want Nix benefits without Nix |
| mise | Languages + tools (Node, Python, Go versions) | Medium | Easy | Small projects, simple stacks |
| Devcontainers | OS + tools (via Docker image) | High | Easy if on Docker | VS Code teams, Codespaces |
Nix flakes — the deep end
Nix is a functional package manager. A flake.nix declaratively pins every dep:
# flake.nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs = { self, nixpkgs }:
let
system = "aarch64-darwin";
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.${system}.default = pkgs.mkShell {
buildInputs = [
pkgs.python313
pkgs.uv
pkgs.postgresql_18
pkgs.redis
pkgs.nodejs_22
pkgs.bun
];
shellHook = ''
export DATABASE_URL=postgres://localhost:5432/mydb
'';
};
};
}
nix develop drops you into a shell with all those exact versions. Every team member gets identical tools, identical libs, identical everything.
Pros:
- Deepest reproducibility. Pins down to the C library level.
- Cross-platform. Linux, macOS, Windows-via-WSL.
- Works without Docker. Direct binaries on your filesystem.
- Composable. Multiple flakes can share inputs.
Cons:
- Learning curve. The Nix language is unique and unforgiving.
- Documentation reputation. Improving but historically painful.
- Mac compatibility. Better in 2026 but some packages still struggle on
aarch64-darwin.
For teams that have one Nix-curious engineer who’ll champion it, Nix is the deepest answer.
Devbox — Nix made friendly
Devbox is a CLI that uses Nix under the hood with a JSON config and a much friendlier UX:
// devbox.json
{
"packages": ["[email protected]", "uv@latest", "postgresql@18", "redis@latest", "nodejs@22", "bun@latest"],
"shell": {
"init_hook": ["export DATABASE_URL=postgres://localhost:5432/mydb"],
"scripts": {
"test": "uv run pytest",
"dev": "uv run uvicorn app.main:app --reload"
}
}
}
devbox shell # drops into the env
devbox run dev # runs the script
Same reproducibility as Nix; vastly simpler config. For 80% of teams, Devbox is the right pick.
For Python-specifically: pair Devbox for the system tools + uv for Python deps. See Modern Python Tooling 2026 .
mise — lightweight version manager
mise (formerly rtx, asdf successor) is a fast tool-version manager. No system libs, just language and CLI tool versions:
# .mise.toml
[tools]
python = "3.13"
node = "22"
go = "1.24"
bun = "latest"
uv = "latest"
mise install # install all pinned versions
mise activate # add to PATH
Pros:
- Fast. Written in Rust.
- Simple. One config file.
- asdf-compatible plugins.
Cons:
- No system libs. Postgres, Redis, etc. need to be installed separately.
- Less reproducible than Nix. A new minor version can leak in.
For simple stacks where the OS-level deps are stable (or run via Docker / Devbox for those), mise is delightful.
Devcontainers — Docker-based
A .devcontainer/devcontainer.json defines a Docker image VS Code spins up automatically:
{
"name": "My Project",
"image": "mcr.microsoft.com/devcontainers/python:3.13",
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "22" },
"ghcr.io/devcontainers/features/docker-in-docker:1": {}
},
"postCreateCommand": "uv sync",
"forwardPorts": [8000, 5432]
}
The dev environment lives in Docker. Identical for every developer. Works in:
- VS Code locally.
- GitHub Codespaces.
- Cloud IDEs (Coder, Gitpod).
Pros:
- Familiar to anyone who knows Docker.
- One-click setup in VS Code.
- Scales to cloud development.
- OS-independent (you get a Linux env on macOS / Windows).
Cons:
- Performance. File IO across the Docker boundary on macOS can be slow.
- VS Code-centric. Other editors have less polish.
- Requires Docker. Not every team wants that.
For VS Code-heavy teams (or teams using Codespaces / Coder), devcontainers are the path of least resistance.
What I’d recommend by team shape
Solo / small team, simple stack
→ mise. .mise.toml in the repo, mise install, done.
Mid team, multiple languages, want reproducibility
→ Devbox. devbox.json, devbox shell, done. Nix benefits without Nix pain.
VS Code or cloud-IDE-centric team
→ Devcontainers. One-click setup; works in Codespaces.
Team with Nix experience or strict reproducibility requirements
→ Nix flakes. The full power.
Big monorepo, multiple stacks, complex builds
→ Nix flakes for the dev shell + Bazel or similar for the build. The two-tool combo handles serious scale.
A pattern that works
Combine tools where they fit:
├── .mise.toml # language/tool versions (every dev needs)
├── devbox.json # system libs + scripts
├── docker-compose.yml # services (Postgres, Redis) — can also live in Devbox
├── .devcontainer/ # for Codespaces
├── flake.nix # for the Nix folks
Pick one as the “primary”; document git clone && devbox shell && devbox run dev. The others can exist as auxiliary paths.
What I’d skip
brew bundle. Pins versions weakly; macOS-only.- Conda. Heavy, opinionated, conflicts with system tools.
asdf. Replaced by mise (faster, better UX).- Setting up everything in
package.jsonscripts. Doesn’t pin versions of the language itself.
CI parity
Whichever tool you pick, use the same one in CI. The .devbox.json runs in GitHub Actions:
- uses: jetify-com/[email protected]
- run: devbox run test
Same for Nix:
- uses: DeterminateSystems/nix-installer-action@main
- run: nix develop -c bash -lc "uv run pytest"
Same env locally and in CI eliminates an entire class of “passes locally fails in CI” bugs.
Getting started
If you’ve never had reproducible dev environments and don’t know where to start:
- Pin language versions with mise in your project.
- Add a README with
mise install && uv sync && uv run pytest(or equivalent). - Add a
docker-compose.ymlfor stateful services (DB, Redis). - After 6 months, when the env starts to drift, upgrade to Devbox for stronger guarantees.
Iterate. Don’t try to reach Nix-grade reproducibility on day one.
Read this next
- Modern Python Tooling 2026
- Modern TypeScript Backend with Hono on Bun
- Platform Engineering and IDPs — dev environments are part of an IDP.
If you want a Devbox + mise + docker-compose template that ships a Python + Node + Postgres + Redis dev stack, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .