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 install ten 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 pinsReproducibilityCurveBest for
Nix flakesEverything (libs, compilers, system utils)HighestSteepComplex monorepos, teams willing to learn
DevboxSame as Nix, friendlierVery highModerateMost teams that want Nix benefits without Nix
miseLanguages + tools (Node, Python, Go versions)MediumEasySmall projects, simple stacks
DevcontainersOS + tools (via Docker image)HighEasy if on DockerVS 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.json scripts. 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:

  1. Pin language versions with mise in your project.
  2. Add a README with mise install && uv sync && uv run pytest (or equivalent).
  3. Add a docker-compose.yml for stateful services (DB, Redis).
  4. 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

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 .