How to build a docker image from a shell.nix?

I’ve got a number of shell.nix coupled with direnv to enable custom environments per project.

If I want to turn those reproducible environments into something easily spun up for those who don’t have nix installed and/or CI/CD environments — what’s the best way to do that?

It seems to me that pkgs.dockerTools.buildImage and pkgs.mkShell are different target outputs that could share a similar setup otherwise.

Suggestions and experience welcome.

here a structure (w/wo mach-nix)

let
  mach-nix = import (builtins.fetchGit {
        url = "https://github.com/DavHau/mach-nix/";
        ref = "refs/tags/3.1.1";
    }) {    };

  nixPkgs =  mach-nix.nixpkgs ; 
  
  envPy = mach-nix.mkPython rec {
    requirements = ''
        dpt-runtime
        dpt-settings>=1,<2
    '';
  };

  env = nixPkgs.buildEnv rec { 
    name = "envTest";       
    paths =[ ((envPy).override( args:{ignoreCollisions=true; }))  
        ] ++ (with nixPkgs; [ bash  ])
        ;
  };

  # shellMk =     nixPkgs.mkShell .. buildInputs = [  (env) ];
  imgOci = nixPkgs.dockerTools.buildLayeredImage {
            name = "test-image";
            contents = [  
              (env) 
            ];
            config = { 
                Cmd = [ "python" "-c"  "import re; print( re.__version__)" ];
            };
          } ;
in imgOci
# in envPy
# in env
# in shellMk

So I ended up using niv and extracting custom derivations into a custom.nix file for now.

Here’s where I got to.

shell.nix

{ sources ? import ./nix/sources.nix,
  pkgs ? import sources.nixpkgs {},
  projectName ? "foobar",
  custom ? (import ./nix/custom.nix {
    inherit
      pkgs
      projectName;
  }),
}:
with pkgs;

let
  baseDir = "${toString ./.}";

in mkShell {
  name = "${projectName}";
  buildInputs = [
    custom
  ];

  shellHook = ''
    echo "Welcome to ${projectName}"
  '';
}

docker.nix

{ sources ? import ./nix/sources.nix,
  pkgs ? import sources.nixpkgs { system = "x86_64-linux"; },
  projectName ? "foobar",
  custom ? (import ./nix/custom.nix {
    inherit
      pkgs
      projectName;
  }),
}:
with pkgs;

let
  baseDir = "${toString ./.}";

in dockerTools.buildImage {
  name = "${projectName}";
  tag = "latest";
  contents = [
    custom
  ];
}

custom.nix

{
  pkgs ? import <nixpkgs> {},
  liquibase ? pkgs.liquibase,
  postgresql ? pkgs.postgresql_12,
  projectName ? "signature-db-migrations",
  ...
}:
with pkgs;

let
  baseDir = "${toString ./.}";
  ...
in buildEnv {
  name = "${projectName}-custom";
  paths = [
    ...
  ];
}
1 Like

What you probably want to use now is streamNixShellImage added in November 2022.
For details see Create docker container from nix shell - #6 by fthomas