# Devbox: Reproducible Development Environments with devbox.json and Nix

[Devbox](https://www.jetpack.io/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.

<iframe width="100%" height="315" src="https://www.youtube.com/embed/U7YDcqP0dlI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>


## How it works

![Terminal window displaying a cascade of setup errors for Node.js, Python, PostgreSQL, and Docker illustrating the failure of a typical README setup guide](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/1234c077-69bf-4b4d-84e5-accca11ada00/public =1280x720)

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.

![Diagram showing the relationship between devbox.json (the declarative file) and devbox.lock (the resolved file) which together create identical builds everywhere](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/194ecb2f-8ca5-480e-3641-b4a1a51de100/orig =1280x720)

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

```command
curl -fsSL https://get.jetpack.io/devbox | bash
```

```command
devbox version
```

## Setting up a project

Initialize a `devbox.json` in any project directory:

```command
devbox init
```

The generated file starts with an empty packages list:

```json
[label devbox.json]
{
  "packages": [],
  "shell": {
    "init_hook": null,
    "scripts": {}
  }
}
```

Add packages with version pins:

```command
devbox add go@1.22
```

```command
devbox add nodejs@20
```

```command
devbox add python@3.11
```

After adding these, `devbox.json` looks like:

```json
[label devbox.json]
{
  "packages": [
    "go@1.22",
    "nodejs@20",
    "python@3.11"
  ],
  "shell": {
    "init_hook": null,
    "scripts": {}
  }
}
```

These packages are not installed globally. They are registered as dependencies for this project only.

Enter the isolated shell:

```command
devbox 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:

```command
devbox search postgresql
```

## Scripts

The `scripts` section in `devbox.json` defines common project commands, similar to `scripts` in `package.json`:


```json
[label devbox.json]
{
  "packages": ["go@1.22", "nodejs@20", "python@3.11"],
  "shell": {
    "init_hook": null,
    "scripts": {
      "test": "echo 'Running tests...' && node --version && go version"
    }
  }
}
```

Running a script:

```command
devbox run test
```

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:

```json
[label devbox.json]
"init_hook": ["npm install"]
```

For complex setup logic, calling a shell script keeps the JSON readable:

![Diagram showing complex logic moved from a JSON string into a clean readable .sh file which is then called from the init_hook](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/8b1b292e-bb2d-40f8-319a-0c4f0aea4100/md2x =1280x720)


```bash
[label setup.sh]
#!/bin/bash
set -e
echo "Installing dependencies..."
npm install
echo "Environment is ready!"
```

```json
[label devbox.json]
"init_hook": ["./setup.sh"]
```

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`:

```command
devbox generate dockerfile
```

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](https://www.jetpack.io/devbox/).