Processes
Devenv provides built-in process management with supervision, socket activation, file watching, and dependency management.
Basic Example
{ 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";
};
};
}
To start the processes, run:
Dependencies
Processes can depend on other processes using after and before:
{
processes = {
database.exec = "postgres";
api = {
exec = "myapi";
after = [ "devenv:processes:database" ]; # wait for database to be ready
};
};
}
Use @complete to wait for a process to stop (soft dependency), or @ready (default) for readiness.
Using Pre-built Services
Devenv provides many pre-configured services with proper process management. See the Services documentation for available services like:
These services come with sensible defaults, health checks, and proper initialization scripts.
Restart Policies
New in devenv 2.0
Control how processes restart when they exit:
on_failure(default) - restart only on non-zero exitalways- restart on any exitnever- never restart
{
processes.worker = {
exec = "worker --queue jobs";
restart = {
on = "always";
max = 10; # null for unlimited (default: 5)
};
};
}
Ready Probes
New in devenv 2.0
Ready 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.
Exec probe
Run a shell command to check readiness. Exit code 0 means ready:
{
processes.database = {
exec = "postgres -D $PGDATA";
ready = {
exec = "pg_isready -d template1";
};
};
}
HTTP probe
Poll an HTTP endpoint for readiness:
{
processes.api = {
exec = "myserver";
ready = {
http.get = {
port = 8080;
path = "/health";
# host = "127.0.0.1"; # default
# scheme = "http"; # default
};
};
};
}
Notify probe
Use systemd-style readiness notification. Your process should send READY=1 to the socket path in $NOTIFY_SOCKET:
{
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:
{
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)
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)
};
};
}
When listen sockets or allocated ports are configured and no explicit probe is set, a TCP connectivity check is used automatically.
File Watching
New in devenv 2.0
Automatically restart processes when files change:
{
processes.backend = {
exec = "cargo run";
watch = {
paths = [ ./src ];
extensions = [ "rs" "toml" ];
ignore = [ "target" "*.log" ];
};
};
}
Socket Activation
New in devenv 2.0
Socket activation allows the process manager to bind sockets before starting your process. This enables zero-downtime restarts and lazy process startup.
{
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 descriptorsLISTEN_PID- PID that should accept the socketsLISTEN_FDNAMES- colon-separated socket names
File descriptors start at 3 (after stdin, stdout, stderr). This is compatible with systemd socket activation.
Watchdog
New in devenv 2.0
Enable systemd-compatible watchdog monitoring. Your process must periodically send WATCHDOG=1 to the notify socket, or it will be killed and restarted:
{
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:
{ config, ... }:
{
processes.frontend = {
exec = "npm run dev";
cwd = "${config.git.root}/frontend";
};
processes.backend = {
exec = "cargo run";
cwd = "${config.git.root}/backend";
};
}
Processes are automatically available as tasks, allowing you to define pre and post hooks. See the Processes as tasks section for details.
Automatic port allocation
New in devenv 2.0
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:
{ 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 until it finds an available port.
Devenv holds the allocated ports during configuration evaluation to prevent race conditions, then releases them just before starting the processes so your application can bind to them.
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, use the --strict-ports flag:
This is useful when you need deterministic port assignments and want to be notified of conflicts rather than having them silently resolved. When a port conflict is detected in strict mode, devenv will show an error message including which process is currently using the port.
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: