Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cachix/devenv/llms.txt

Use this file to discover all available pages before exploring further.

devenv provides built-in process management with supervision, socket activation, file watching, and dependency management. You can define any number of named processes in devenv.nix, start them all with a single command, and coordinate their startup order using dependency declarations and readiness probes.

Defining Processes

Add a processes block to devenv.nix. Each process needs at minimum an exec command:
devenv.nix
{ pkgs, ... }:

{
  processes = {
    silly-example.exec = "while true; do echo hello && sleep 1; done";
    ping.exec = "ping localhost";
    server = {
      exec = "python -m http.server";
      cwd = "./public";
    };
  };
}

Starting and Stopping Processes

1
Start all processes
2
$ devenv up
3
Stop detached processes
4
$ devenv down
5
devenv down is a shorthand for devenv processes down, available since devenv 2.1.3.
6
Wait for processes to become ready (useful in CI)
7
$ devenv processes wait --timeout 120
8
The default timeout is 120 seconds.

Dependencies

Processes can depend on other processes and tasks using after and before:
devenv.nix
{
  processes = {
    database.exec = "postgres";

    api = {
      exec = "myapi";
      after = [ "devenv:processes:database" ];  # wait for database to be ready
    };
  };
}

Dependency Suffixes

Dependency suffixes control when a dependency is considered satisfied. For process dependencies:
SuffixSatisfied when
@startedthe process has begun execution
@ready (default)the readiness probe passes
@completedthe process finishes, regardless of exit code (soft dependency)
For task dependencies:
SuffixSatisfied when
@startedthe task has begun execution
@succeeded (default)the task exits with code 0
@completedthe task finishes, regardless of exit code (soft dependency)
devenv up schedules processes in before mode, which runs each process’s upstream dependencies but not tasks that run after it. A setup or configure task wired downstream of a process is skipped under devenv up and never runs. Use devenv up --mode all, or see the Tasks documentation for details.

Restart Policies

Control how processes restart when they exit:
PolicyBehavior
on_failure (default)restart only on non-zero exit
alwaysrestart on any exit
nevernever restart
devenv.nix
{
  processes.worker = {
    exec = "worker --queue jobs";
    restart = {
      on = "always";
      max = 10;  # null for unlimited (default: 5)
    };
  };
}

Readiness Probes

Readiness probes let the process manager detect when a process is ready to serve. This is used by after dependencies to know when a dependency is available.
Run a shell command to check readiness. Exit code 0 means ready:
devenv.nix
{
  processes.database = {
    exec = "postgres -D $PGDATA";
    ready = {
      exec = "pg_isready -d template1";
    };
  };
}
Poll an HTTP endpoint for readiness:
devenv.nix
{
  processes.api = {
    exec = "myserver";
    ready = {
      http.get = {
        port = 8080;
        path = "/health";
        # host = "127.0.0.1";  # default
        # scheme = "http";     # default
      };
    };
  };
}
Use systemd-style readiness notification. Your process should send READY=1 to the socket path in $NOTIFY_SOCKET:
devenv.nix
{
  processes.database = {
    exec = "postgres";
    ready.notify = true;
  };

  processes.api = {
    exec = "myapi";
    after = [ "devenv:processes:database" ];  # waits for READY=1
  };
}

Probe Timing Options

All probe types support these timing options:
devenv.nix
{
  processes.api = {
    exec = "myserver";
    ready = {
      http.get = { port = 8080; path = "/health"; };
      initial_delay = 2;    # seconds before first probe (default: 0)
      period = 10;          # seconds between probes (default: 10)
      probe_timeout = 1;    # seconds before probe times out (default: 1)
      success_threshold = 1; # consecutive successes needed (default: 1)
      failure_threshold = 3; # consecutive failures before unhealthy (default: 3)
      # timeout = ;         # overall deadline in seconds; null = no deadline
    };
  };
}
When listen sockets or allocated ports are configured and no explicit probe is set, a TCP connectivity check is used automatically.

Socket Activation

Socket activation allows the process manager to bind sockets before starting your process, enabling zero-downtime restarts and lazy process startup.
devenv.nix
{
  processes.api = {
    exec = "myserver";
    listen = [
      {
        name = "http";
        kind = "tcp";
        address = "127.0.0.1:8080";
      }
      {
        name = "admin";
        kind = "unix_stream";
        path = "$DEVENV_STATE/admin.sock";
      }
    ];
  };
}
Your process receives these environment variables:
  • LISTEN_FDS — number of passed file descriptors
  • LISTEN_PID — PID that should accept the sockets
  • LISTEN_FDNAMES — colon-separated socket names
File descriptors start at 3 (after stdin, stdout, stderr). This is compatible with systemd socket activation.

File Watching

Automatically restart processes when files change:
devenv.nix
{
  processes.backend = {
    exec = "cargo run";
    watch = {
      paths = [ ./src ];
      extensions = [ "rs" "toml" ];
      ignore = [ "target" "*.log" ];
    };
  };
}
This works for both long-running processes and one-shot commands. A long-running process (such as cargo run) is restarted on each change. A one-shot command that exits immediately is re-run on each change — the watcher stays active after the command exits.
devenv.nix
{
  # Prints a line every time a file in ./src changes.
  processes.on-change = {
    exec = "echo 'a file in ./src changed'";
    watch = {
      paths = [ ./src ];
    };
  };
}
watch.paths entries are resolved relative to the location of your devenv.nix (the project root), not relative to the process’s cwd. Use path literals such as ./src rather than strings; they are passed to the watcher as absolute paths.

Automatic Port Allocation

devenv can automatically allocate free ports for your processes, preventing conflicts when a port is already in use or when running multiple devenv projects simultaneously. Define ports using ports.<name>.allocate with a base port number. devenv will find a free port starting from that base, incrementing until one is available:
devenv.nix
{ config, ... }:

{
  processes.server = {
    ports.http.allocate = 8080;
    ports.admin.allocate = 9000;
    exec = ''
      echo "HTTP server on port ${toString config.processes.server.ports.http.value}"
      echo "Admin panel on port ${toString config.processes.server.ports.admin.value}"
      python -m http.server ${toString config.processes.server.ports.http.value}
    '';
  };
}
The resolved port is available via config.processes.<name>.ports.<port>.value. If port 8080 is already in use, devenv will automatically try 8081, 8082, and so on. This is particularly useful for:
  • Running multiple projects — each project gets its own ports without manual coordination
  • CI environments — tests can run in parallel without port conflicts
  • Shared development machines — multiple developers can run the same project simultaneously

Strict Port Mode

If you want devenv to fail when a port is already in use instead of automatically finding the next available port, set the default in devenv.yaml:
devenv.yaml
strict_ports: true
Or override it for a single run with CLI flags:
$ devenv up --strict-ports
$ devenv up --no-strict-ports
CLI flags take precedence over the config value.

Watchdog

Enable systemd-compatible watchdog monitoring. Your process must periodically send WATCHDOG=1 to the notify socket, or it will be killed and restarted:
devenv.nix
{
  processes.api = {
    exec = "myserver";
    ready.notify = true;
    watchdog = {
      usec = 30000000;      # 30 seconds
      require_ready = true;  # only enforce after READY=1 (default)
    };
  };
}

Git Integration

Processes can reference the git repository root path using ${config.git.root}, useful in monorepo environments:
devenv.nix
{ config, ... }:

{
  processes.frontend = {
    exec = "npm run dev";
    cwd = "${config.git.root}/frontend";
  };

  processes.backend = {
    exec = "cargo run";
    cwd = "${config.git.root}/backend";
  };
}

Processes as Tasks

Processes are automatically available as tasks with the devenv:processes: prefix. This allows you to define pre and post hooks, create complex startup sequences, and use before/after dependency edges between tasks and processes interchangeably. See the Tasks documentation for details.

Alternative Process Managers

By default, devenv uses its native process manager. You can switch to alternative implementations:
  • process-compose — feature-rich external process manager with TUI
  • overmind — Procfile-based with tmux integration
  • honcho — Python Foreman port
  • hivemind — simple Procfile manager
  • mprocs — TUI process manager
To switch:
devenv.nix
{
  process.manager.implementation = "process-compose";
}

Build docs developers (and LLMs) love