Skip to content

Containers

Added in 0.6

To use ‘containers’ command, add the following dependencies:

Terminal window
$ devenv inputs add nix2container github:nlewo/nix2container --follows nixpkgs
$ devenv inputs add mk-shell-bin github:rrbutani/nix-mk-shell-bin

Use devenv container build <name> to generate an OCI container from your development environment.

By default, shell and processes containers are predefined. You can also craft your own!

Examples of what devenv container can do:

  • devenv container build shell: Generate a container and start the environment, equivalent of using devenv shell.
  • devenv container build processes: Generate a container and start processes, equivalent of using devenv up.
  • devenv container --registry docker://ghcr.io/ copy <name>: Copy the container <name> into the GitHub package registry.
  • devenv container run <name>: Run the container <name> using Docker.

See the list of all container options.

Given a simple environment, using Python:

devenv.nix
{
name = "simple-python-app";
languages.python.enable = true;
}

Generate a container specification that enters the environment:

Terminal window
$ devenv container build shell
/nix/store/...-image-devenv.json

Let’s test it locally using Docker:

Terminal window
$ 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.
>>>

A common deployment strategy is to run each process as an entrypoint to the container.

devenv.nix
{
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;
}

You can now copy the newly created image and start the container:

Terminal window
$ 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!

You can specify the command to run when the container starts (instead of entering the default development environment):

devenv.nix
{
processes.serve.exec = "python -m http.server";
containers."serve" = {
name = "myapp";
startupCommand = config.processes.serve.exec;
};
}
Terminal window
$ devenv container run serve

If you’re building binaries as part of the development environment, you can choose to only include those in the final image:

devenv.nix
{
# 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";
};
}
Terminal window
$ devenv container run prod
...

To copy a container into a registry use copy subcommand:

Terminal window
$ devenv container --registry docker:// copy processes

Another common example is deploying to fly.io. Any arguments passed to --copy-args are forwarded to skopeo copy:

Terminal window
$ devenv container --registry docker://registry.fly.io/ --copy-args="--dest-creds x:$(flyctl auth token)" copy processes

You can also specify these options declaratively:

devenv.nix
{
containers."processes" = {
registry = "docker://registry.fly.io/";
defaultCopyArgs = [
"--dest-creds"
"x:\"$(${pkgs.flyctl}/bin/flyctl auth token)\""
];
};
}

See this fly.io example for how to get started.

Changing the environment based on the build type

Section titled “Changing the environment based on the build type”

If you want to provide the openssl package to native and container environments, but git only for native environments:

devenv.nix
{ pkgs, config, lib, ... }:
{
packages = [ pkgs.openssl ]
++ lib.optionals (!config.container.isBuilding) [ pkgs.git ];
}

You can also conditionalize based on the particular container that is being built, for example, config.containers."processes".isBuilding.