For devenv, our mission is to make Nix the ultimate tool for managing developer environments. Nix
excels at congruent configuration,
where the system state is fully described by declarative code.
However, the real world often throws curveballs. Side-effects like database migrations, one-off
tasks such as data imports, or external API calls don’t always fit neatly into this paradigm.
In these cases, we often resort to convergent configuration,
where we define the desired end-state and let the system figure out how to get there.
To bridge this gap and make Nix more versatile, we’re introducing tasks. These allow you to
handle those pesky real-world scenarios while still leveraging Nix’s powerful ecosystem.
We’ve talked to many teams that dropped Nix after a while and they usually fit into two categories:
Maintaining Nix was too complex and the team didn’t fully onboard, creating friction inside the teams.
Went all-in Nix and it took a big toll on the team productivity.
While devenv already addresses (1), bridging the gap between Nix provided developer environments
and existing devops tooling written in your favorite language is still an unsolved problem until now.
We’ve designed Task Server Protocol so that you can write tasks
using your existing automation by providing an executable that exposes the tasks to devenv:
In a few weeks we’re planning to provide Rust TSP SDK
with a full test suite so you can implement your own abstraction in your language of choice.
You can now use your preferred language for automation, running tasks with a simple devenv tasks run <names> command. This
flexibility allows for more intuitive and maintainable scripts, tailored to your team’s familiarity.
For devenv itself, we’ll slowly transition from bash to Rust for
internal glue code, enhancing performance and reliability. This change will make devenv more
robust and easier to extend, ultimately providing you with a smoother development experience.
If you import another devenv.nix file, the outputs will be merged together,
allowing you to compose a developer environment and outputs in one logical unit.
You could also import outputs from other applications as inputs instead of composing them.
When I started to write this blog post for the Python rewrite, I came up with only excuses as to why it is not fast and realized that we were simply breaking our promise to you.
The second reason is that in the Nix community there has been a lot of controversy surrounding flakes (that’s for another blog post); two years ago, the tvix developers decided to do something about it and started a rewrite of Nix in Rust. This leaves us with the opportunity in the future to use the same Rust libraries and tooling.
devenv up is now using process-compose,
as it handles dependencies between processes and provides a nice ncurses interface to view the processes
and their logs.
Since nixpkgs-unstable has fairly few tests,
we have created devenv-nixpkgs to run tests on top of nixpkgs-unstable—applying patches we are upstreaming to address any issues.
We run around 300 tests across different languages and processes to ensure all regressions are caught.
Due to socket path limits, the DEVENV_RUNTIME environment variable has been introduced: pointing to $XDG_RUNTIME_DIR by default and falling back to /tmp.
If you need to add an input to devenv.yaml, you can now do:
devenv inputs add <name> <url>
To update a single input:
devenv update <input>
To build any attribute in devenv.nix:
devenv build languages.rust.package
To run the environment as cleanly as possible while keeping specific variables:
devenv shell --clean EDITOR,PAGER
The default number of cores has been tweaked to 2, and max-jobs to half of the number of CPUs.
It is impossible to find an ideal default, but we have found that too much parallelism hurts performance—running out of memory is a common issue.
… plus a number of other additions:
https://devenv.sh 1.0.0: Fast, Declarative, Reproducible, and Composable Developer Environments
Usage: devenv [OPTIONS] <COMMAND>
Commands:
init Scaffold devenv.yaml, devenv.nix, .gitignore and .envrc.
shell Activate the developer environment. https://devenv.sh/basics/
update Update devenv.lock from devenv.yaml inputs. http://devenv.sh/inputs/
search Search for packages and options in nixpkgs. https://devenv.sh/packages/#searching-for-a-file
info Print information about this developer environment.
up Start processes in the foreground. https://devenv.sh/processes/
processes Start or stop processes.
test Run tests. http://devenv.sh/tests/
container Build, copy, or run a container. https://devenv.sh/containers/
inputs Add an input to devenv.yaml. https://devenv.sh/inputs/
gc Deletes previous shell generations. See http://devenv.sh/garbage-collection
build Build any attribute in devenv.nix.
version Print the version of devenv.
help Print this message or the help of the given subcommand(s)
Options:
-v, --verbose
Enable debug log level.
-j, --max-jobs <MAX_JOBS>
Maximum number of Nix builds at any time. [default: 8]
-j, --cores <CORES>
Maximum number CPU cores being used by a single build.. [default: 2]
-s, --system <SYSTEM>
[default: x86_64-linux]
-i, --impure
Relax the hermeticity of the environment.
-c, --clean [<CLEAN>...]
Ignore existing environment variables when entering the shell. Pass a list of comma-separated environment variables to let through.
-d, --nix-debugger
Enter Nix debugger on failure.
-n, --nix-option <NIX_OPTION> <NIX_OPTION>
Pass additional options to nix commands, see `man nix.conf` for full list.
This would be convenient when the environment is too complex to set up on your local machine; for example, when running two databases or when you want to run tests in a clean environment.
Wouldn’t it be cool if devenv could map language-specific dependencies to your local system? In this example, devenv should be able to determine that pillow requires pkgs.cairo:
Especially monorepo developer environments can sometimes be even a few gigabytes of size, taking a few seconds for the environment to be activated.
A developer environment should only be built
when something changes and if not, the environment
can be used instantly using a cached snapshot.
With the latest direnv.net integration,
we’ve finally reached that goal by making caching work properly (it will even watch each of your imports for changes!).
In the near future we’ll experiment to improve devenv shell experience.
Rust language support now integrates with fenix to provide stable/nightly/unstable toolchain for cargo, rustc, rust-src, rust-fmt, rust-analyzer and clippy.
Python language now sets $PYTHONPATH to point to any installed packages in packages attribute.
Ruby langauge support now defaults to the latest version 3.1.x, ships with an example running rails, sets $GEM_HOME and $GEM_PATH environment variables. Next release will support picking any version of Ruby - please leave a thumbs up.