TL;DR:

  • devenv.sh is a Nix-powered developer environment tool that handles package installation, service startup (databases, queues, dev servers), shell configuration, and pre-commit hooks in a single devenv.nix file
  • It sits between devbox (simpler YAML-based Nix tool) and raw Nix flakes (more powerful but steeper learning curve) — using Nix’s package set and reproducibility guarantees with a developer-experience-focused API
  • devenv up starts all configured services; devenv shell enters the environment; direnv integration means the environment activates automatically when you enter the project directory

Most development environment problems fall into two categories: what packages are installed, and what services are running. Getting both of these right, in a way that works the same for every developer on a team and doesn’t drift over time, turns out to be harder than it should be.

Docker Compose solves the services problem for many teams — but it adds container overhead, runs services in isolation from the host, and requires Docker to be running. Shell scripts solve the package problem badly. .env files and brew bundle help but don’t guarantee reproducibility. devcontainers get you reproducibility in a container but with more friction and slower startup.

devenv.sh takes a different approach: use Nix to get genuine reproducibility, but wrap it in an API that doesn’t require understanding Nix internals to be productive.

What devenv.nix Looks Like

The core of devenv is a devenv.nix file in your project root. Here’s what a typical Node.js + PostgreSQL project looks like:

{ pkgs, ... }:

{
  packages = [
    pkgs.nodejs_22
    pkgs.nodePackages.pnpm
    pkgs.postgresql_16
    pkgs.redis
  ];

  languages.javascript = {
    enable = true;
    npm.enable = true;
  };

  services.postgres = {
    enable = true;
    port = 5432;
    initialScript = "CREATE DATABASE myapp;";
  };

  services.redis = {
    enable = true;
    port = 6379;
  };

  env = {
    DATABASE_URL = "postgresql://localhost:5432/myapp";
    REDIS_URL = "redis://localhost:6379";
  };

  pre-commit.hooks = {
    eslint.enable = true;
    prettier.enable = true;
  };

  enterShell = ''
    echo "Dev environment ready. Run 'devenv up' to start services."
  '';
}

When a developer runs devenv shell, they get the packages from nixpkgs, the environment variables, and the shell hook. When they run devenv up, PostgreSQL and Redis start as processes managed by devenv’s process-compose supervisor. Everything is defined in one file, and that file is committed to the repository.

Installation and Setup

Nix must be installed first. The recommended path uses the Determinate Systems Nix installer, which handles macOS and Linux:

curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install

Once Nix is available:

nix profile install github:cachix/devenv/latest

In your project:

devenv init

This creates a starter devenv.nix and a devenv.lock lockfile (which pins all package versions for reproducibility) and a .envrc for direnv integration.

With direnv installed and .envrc trusted:

echo "use devenv" > .envrc
direnv allow

After this, entering the project directory in a new shell activates the environment automatically — packages are available, environment variables are set, everything is ready without running any commands.

The Process Management Piece

This is where devenv differentiates from pure package managers. The services module knows how to configure and start common development services:

  • PostgreSQL: data directory, port, initial database setup
  • Redis: port and configuration
  • MySQL/MariaDB: similar to PostgreSQL
  • Elasticsearch/OpenSearch: for search development
  • MongoDB: for document store scenarios
  • Nginx: for reverse proxy configurations
  • Meilisearch: for local search indexing

Beyond the built-in services, the processes module lets you define arbitrary processes:

processes = {
  web.exec = "npm run dev";
  worker.exec = "npm run worker";
  tailwind.exec = "npm run tailwind:watch";
};

devenv up starts all of these together in a process-compose view showing logs, status, and restarts. Ctrl+C stops everything cleanly. For teams that previously relied on a combination of multiple terminal tabs, a Makefile, and Procfile, this is a meaningful improvement.

How It Compares

devbox (from Jetpack.io) is the simplest entry point to Nix-based environments. You define packages in a JSON file with no Nix syntax. devbox is lower friction to get started with, but it doesn’t support the full range of devenv features — particularly the service management and the per-project environment customisation through Nix expressions. If you just want “install these packages reproducibly,” devbox is easier. If you need service management and more flexible configuration, devenv is the right tool.

devcontainers give you a fully isolated environment in a Docker container. The reproducibility is stronger (the container image pins everything), but the isolation comes at a cost: slower startup, more memory usage, and friction when you need host system access (GPU, hardware devices, complex networking). devenv runs natively — packages live in the Nix store on your machine, and services run as regular processes. For most development scenarios, the native approach is faster and easier.

Nix flakes are the underlying mechanism that devenv is built on. Raw Nix flakes are more powerful — you can express anything Nix can express — but the learning curve for developers unfamiliar with Nix is steep. devenv’s module system abstracts the most common patterns (packages, services, language tooling) into a cleaner interface. For teams who need the full power of Nix flakes, they can be used directly; devenv is for teams who want the benefits without the investment.

mise is a runtime version manager (managing Node.js, Python, Ruby versions, etc.) rather than a full environment manager. mise and devenv can be complementary — mise for runtime version switching across projects, devenv for complete environment + services setup within a project.

Practical Adoption

The main friction with devenv is the Nix prerequisite. Teams that haven’t used Nix before often react to the installation step — Nix is a non-trivial addition to a development machine. In practice, the Determinate Systems installer is straightforward, and once Nix is present, devenv itself installs in seconds. The bigger investment is writing the initial devenv.nix for a project.

For existing projects, the payoff is clearest on projects with service dependencies. If your project’s README has a section called “Local Setup” with ten steps — install PostgreSQL, create a database, set environment variables, install Redis, configure it, install Node.js via nvm, run npm install — devenv replaces that with devenv shell && devenv up. New developers can be productive in minutes rather than hours.

For projects with simpler dependencies, devenv is still useful but the benefit is smaller. Package pinning and reproducible shells help but don’t transform the onboarding experience as dramatically.

The devenv documentation at devenv.sh is well-maintained and includes module references for all the built-in services and language configurations. The nixpkgs package count is extensive — most development tools you’d need are available without writing custom derivations.