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 can generate OCI-compatible containers directly from your development environment using nix2container. No Dockerfile required — the same devenv.nix that defines your shell also defines what goes into the image.
Containers were introduced in devenv v0.6.
To build containers on macOS, you need a remote Linux builder. The easiest options are:
The containers command requires two additional inputs. Add them with:
$ devenv inputs add nix2container github:nlewo/nix2container --follows nixpkgs
$ devenv inputs add mk-shell-bin github:rrbutani/nix-mk-shell-bin
Predefined containers
By default, devenv provides two ready-made containers:
| Name | Equivalent command | Description |
|---|
shell | devenv shell | Enters the development environment |
processes | devenv up | Starts all defined processes |
Build either one with devenv container build <name>.
Entering the development environment
Given a simple environment:
{
name = "simple-python-app";
languages.python.enable = true;
}
Build the shell container:
$ devenv container build shell
/nix/store/...-image-devenv.json
Run it locally using Docker to verify:
$ devenv container run shell
...
(devenv) bash-5.2# python
Python 3.10.9 (main, Dec 6 2022, 18:44:57) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Running processes
A common deployment strategy is to run each process as an entrypoint to a container:
{
name = "myapp";
packages = [ pkgs.procps ];
processes = {
hello-docker.exec = "while true; do echo 'Hello Docker!' && sleep 1; done";
hello-nix.exec = "while true; do echo 'Hello Nix!' && sleep 1; done";
};
# Exclude the source repo to make the container smaller.
# containers.processes.copyToRoot = null;
}
Build and run the processes container:
$ devenv container run processes
...
06:30:06 system | hello-docker.1 started (pid=15)
06:30:06 hello-docker.1 | Hello Docker!
06:30:06 system | hello-nix.1 started (pid=16)
06:30:06 hello-nix.1 | Hello Nix!
06:30:07 hello-nix.1 | Hello Nix!
06:30:07 hello-docker.1 | Hello Docker!
06:30:08 hello-nix.1 | Hello Nix!
06:30:08 hello-docker.1 | Hello Docker!
Custom containers
Running a single process
Specify exactly which command runs when the container starts:
{
processes.serve.exec = "python -m http.server";
containers."serve" = {
name = "myapp";
startupCommand = config.processes.serve.exec;
};
}
$ devenv container run serve
Running build artifacts
If you build binaries as part of the development environment, you can include only those in the final image:
{
# Watch local changes and build the project to ./dist
processes.build.exec = "${pkgs.watchexec}/bin/watchexec my-build-tool";
containers."prod" = {
copyToRoot = ./dist;
startupCommand = "/mybinary serve";
};
}
$ devenv container run prod
...
Copying a container to a registry
Use the copy subcommand with --registry to push a container image:
$ devenv container --registry docker:// copy processes
Deploying to fly.io
Any arguments passed to --copy-args are forwarded to skopeo copy:
$ devenv container --registry docker://registry.fly.io/ --copy-args="--dest-creds x:$(flyctl auth token)" copy processes
You can also declare these options in devenv.nix to avoid repeating them on every deploy:
{
containers."processes" = {
registry = "docker://registry.fly.io/";
defaultCopyArgs = [
"--dest-creds"
"x:\"$(${pkgs.flyctl}/bin/flyctl auth token)\""
];
};
}
See the fly.io example for a complete getting-started setup.
Changing behaviour based on build type
Use config.container.isBuilding to conditionally include packages or settings that should only apply to native (non-container) environments:
{ pkgs, config, lib, ... }:
{
packages = [ pkgs.openssl ]
++ lib.optionals (!config.container.isBuilding) [ pkgs.git ];
}
You can also target a specific container by name — for example, config.containers."processes".isBuilding — to customise behaviour for just that image.