Devbox: Reproducible Development Environments with devbox.json and Nix
Devbox is a command-line tool that creates isolated development shells from a devbox.json file. It uses the Nix Package Manager under the hood but exposes a simple interface without requiring knowledge of the Nix language. Package versions are resolved to a devbox.lock file, so any developer running devbox shell from the same repository gets bit-for-bit identical tool versions.
How it works
Devbox installs packages in a sandboxed environment, not globally. When you run devbox shell, the specified tools are placed on your PATH for the duration of the session. When you exit, your global environment is unchanged.
Two files control the environment:
devbox.json: the human-readable declaration you edit (for example,"nodejs@20","go@1.22")devbox.lock: auto-generated by Devbox with exact Nix package hashes
Committing both to the repository ensures that any developer or CI job using the same commit gets the same environment.
The Nix package repository contains over 400,000 package versions, including specific minor versions of most programming languages, databases, and CLI tools. Multiple versions of the same tool can coexist on a single machine without conflict because Nix isolates them at the filesystem level.
Installation
Setting up a project
Initialize a devbox.json in any project directory:
The generated file starts with an empty packages list:
Add packages with version pins:
After adding these, devbox.json looks like:
These packages are not installed globally. They are registered as dependencies for this project only.
Enter the isolated shell:
The first run downloads packages from Nix and caches them. Subsequent starts are instantaneous. Once inside the shell, go version, node --version, and python --version reflect the pinned versions. Running exit returns to the normal environment.
To search for available packages:
Scripts
The scripts section in devbox.json defines common project commands, similar to scripts in package.json:
Running a script:
This starts a Devbox shell, executes the script in the correct environment, and exits. Scripts always run with the project's pinned tool versions regardless of what is installed globally.
init_hook for setup automation
init_hook runs commands each time a developer enters the shell. It is useful for installing language-specific dependencies or setting environment variables.
For simple cases, inline commands work:
For complex setup logic, calling a shell script keeps the JSON readable:
The setup.sh approach makes the setup logic version-controllable and reviewable as a normal file rather than an embedded string.
Generating a Dockerfile
If the project requires a Docker image (for example, for VS Code Dev Containers), Devbox can generate one from devbox.json:
This produces a Dockerfile that installs the same packages specified in the project's devbox.json, giving a single source of truth for both the local shell and the container image.
CI/CD integration
Because devbox.lock pins exact versions, running devbox shell in a CI job produces the same environment as a developer's local machine. This eliminates the class of failures where a test passes locally but fails in CI due to a different tool version.
Final thoughts
Devbox solves the version management problem without requiring Docker for local development. It is faster than containers for day-to-day shell use, integrates with Git in a straightforward way, and does not require learning Nix directly.
The two-file model (devbox.json for declarations, devbox.lock for resolution) is the same pattern used by most modern dependency managers and is easy to reason about in code review. When a dependency is updated, the diff in devbox.lock shows exactly what changed.
For teams where setup time and "works on my machine" issues are recurring costs, the one-time effort of adding a devbox.json to each project pays back quickly.
Documentation is at jetpack.io/devbox.