devenv 2.3: Portless and TUI configuration
devenv 2.0 introduced automatic port allocation. Your dev server starts even when another project is already using port 3000. But now it’s on 3001, your browser still points to 3000, and you’re looking at the wrong app.
devenv 2.3 gives your processes stable <process>.<project>.localhost URLs and makes the terminal interface configurable, from statusline placement and colors to keybindings and log behavior.
Portless
Section titled “Portless”Enable the localhost proxy and declare a port for your web server (devenv#3141):
{ pkgs, config, ... }:
{ process.proxy.enable = true;
processes.web = { exec = "${pkgs.python3}/bin/python -m http.server $PORT"; ports.http.allocate = 8000; env.PORT = builtins.toString config.processes.web.ports.http.value; };}Run devenv up and open the URL shown in the TUI: http://web.myapp.localhost for a project named myapp. The URL stays the same even when devenv chooses a different port.
The shared proxy is built on Pingora, Cloudflare’s Rust framework for building proxies and network services. devenv manages the routes as part of its native process manager.
On Linux, devenv asks for sudo authentication to let the proxy listen on port 80.
Choose your own hostnames
Section titled “Choose your own hostnames”Override a process’s hostname with a full .localhost name:
{ processes.web.proxy.hostname = "app.localhost";}Processes with multiple named ports get routes such as http://admin.app.localhost. Each port can also have its own hostname:
{ processes.web.ports.http.proxy.hostname = "public.localhost"; processes.web.ports.admin.proxy.hostname = "control.localhost";}HTTPS when you need it
Section titled “HTTPS when you need it”Enable HTTPS per process:
{ process.proxy.enable = true; processes.web.proxy.https.enable = true;}devenv generates local certificates with mkcert and shows the HTTPS URL in the TUI. Your application keeps serving HTTP; the proxy handles HTTPS for you.
The first setup may ask you to trust the local certificate authority. Restart an already running proxy when first enabling HTTPS.
See friendly localhost URLs for the full configuration.
Linux capabilities
Section titled “Linux capabilities”For services that need to bind a privileged port themselves, the native process manager can grant Linux capabilities while the service keeps running as your user (devenv#3151):
{ processes.web = { exec = "caddy run"; linux.capabilities = [ "net_bind_service" ]; };}devenv shows the requested capabilities and authenticates with sudo once.
Your terminal, your configuration
Section titled “Your terminal, your configuration”Two common requests since 2.0: let me keep my own shell prompt, and let me hide the statusline. In 2.3, you can do both (devenv#3117):
version: 1shell: prompt_prefix: falsetui: statusline: enabled: falseshell.prompt_prefix: false removes the (devenv) prefix from your shell prompt. tui.statusline.enabled: false hides the statusline, including the persistent bar in devenv shell. Set either one or both.
These are personal preferences that apply across projects. Save the file at ~/.config/devenv/config.yaml, or $XDG_CONFIG_HOME/devenv/config.yaml if you’ve set it, and start a new shell.
You can also change statusline placement and colors, remap shortcuts, and adjust log behavior. See TUI customization for the full configuration.
Run devenv user-config validate to check the file, including key conflicts and statusline formats. Add # yaml-language-server: $schema=https://devenv.sh/devenv.user.schema.json at the top for editor completion.
The log viewer also gained fullscreen search with highlighted matches, vim style scrolling, and a copied line counter.
Pin many package versions at once
Section titled “Pin many package versions at once”devenv 2.2 introduced nixpkgs-multiverse pins such as multiverse.cmake."3.16.5". Each pin resolved on its own, so five pins could mean five nixpkgs revisions to fetch and evaluate. multiverse.pins resolves the whole set through the fewest revisions that can serve every requested version:
{ multiverse, ... }:
{ packages = multiverse.pins { cmake = "3.26.4"; bun = "0.7.0"; };}You get exactly those versions, and Farid Zakaria’s write up explains why the selection is minimal. See pinning for details.
And more
Section titled “And more”SecretSpec 0.20. Git and Docker credential helpers, inline secret specifications, and five new providers: Azure App Configuration, Kubernetes, EJSON, Fly.io, and Cloudflare Secrets Store. See the release announcement for details.
Better dotenv support. The new dotenv-ng parser runs in the devenv CLI and handles quotes, multiline values, comments, export, and optional variable substitution. It supports ordered loading of several files, files in subdirectories, and files generated by tasks. Dotenv changes participate in evaluation caching and shell hot reloads, while explicit env definitions retain precedence. Older CLIs fall back to the legacy parser when using newer modules.
Arguments for auto-activated shells. Forward shell arguments through the native hook, for example devenv hook fish -- --no-tui (devenv#3128). Bash, zsh, fish, and nushell are supported.
Configurable process shutdown. processes.<name>.shutdown.signal and .grace control how the native manager and process-compose stop and restart a process. PostgreSQL now uses SIGINT for fast shutdown.
Faster garbage collection. With a Nix daemon running 2.35 or newer, devenv gc removes old environments in a single batch and shows progress. The bundled Nix is now 2.35.2.
More reliable cleanup. Processes started as task dependencies are stopped when devenv tasks run exits. A second Ctrl+C no longer abandons processes during shutdown, and temporary shell capture scripts no longer accumulate in .devenv.
Recovery after crashes. A guardian cleans abandoned service sessions, and the next manager reconciles them before starting the same process again. Detached manager state is also kept in .devenv, so devenv processes down works after logging back in.
External process managers. Detached mode is supported by process-compose, Honcho, Hivemind, and Overmind. Unsupported operations fail before launch, and devenv down gracefully stops Overmind and waits for it to exit.
Smaller closure and faster shells. The devenv closure shrank from 528 MB to 376 MB by removing duplicate dependencies. Git hook installation is skipped when installed hooks already match, file watching uses fewer allocations, and GC roots survive moving a project directory.
More useful traces. Process ports, readiness probes, exits, and restarts now appear as structured trace data. OTLP exports Nix evaluator heap and garbage collection metrics, and trace serialization uses fewer allocations.
Better diagnostics. Module errors point at the file that defined the offending option, and devenv tasks list prints a tree with inline descriptions. The test harness reports runtime and shell closure size and can enforce a max_closure_size limit.
See the full changelog for the rest.
Final words
Section titled “Final words”Open an issue or join the Discord with feedback.
Domen

