Building a docker image is much simpler with Nix compared to writing Dockerfile
. Since the entire build process is handled by Nix flakes, most of what’s left to do for docker image creation is copying of the derivations and configuration.
Writing the Nix to build the docker image
Consider a haskell-flake project “foo”. To copy the binaries generated by the default
package to /bin
on the image, one can use copyToRoot
attribute offered by dockerTools.buildImage
. For example:
{
# Inside perSystem
packages.dockerImage = pkgs.dockerTools.buildImage {
name = "foo";
copyToRoot = pkgs.buildEnv {
paths = with pkgs; [
self'.packages.default
];
name = "foo-root";
pathsToLink = [ "/bin" ];
};
};
}
In addition to copying over the flake packages
, we may also copy paths in the project. self
can be added to paths
to expose the project directory.
{
copyToRoot = pkgs.buildEnv {
paths = with pkgs; [
coreutils
bash
self
];
name = "foo-root";
pathsToLink = [ "/foo_sub" "/bin" ];
};
}
If you’d like your docker image to run your haskell project’s default package when the container starts, use the following config:
{
# Inside dockerImage's `buildImage`
config = {
Cmd = [ "${pkgs.lib.getExe self'.packages.default}" ];
};
}
Build the docker image
To build the docker image as a Nix derivation, run:
nix build .#dockerImage
To load this image into your local docker image registry, run:
docker load -i $(nix build .#dockerImage --print-out-paths)
Tips
Size
Docker images including Haskell packages can be optimized using the methods described here.
Time
If you don’t want docker images
showing that the image was created several decades ago, use the following:
{
# Inside perSystem.packages' `dockerImage`:
pkgs.dockerTools.buildImage {
name = "foo";
created = "now";
};
}
Tag
If you want to tag the images with the commit id of the working copy:
{
# Inside perSystem.packages' `dockerImage`:
pkgs.dockerTools.buildImage {
name = "foo";
tag = builtins.substring 0 9 (self.rev or "dev");
};
}
builtins.substring 0 9 self.rev
is the same as git rev-parse --short HEAD
. self.rev
is non-null only on a clean working copy and hence the tag is set to dev
when the working copy is dirty.
SSL certs
In order to be able to make https connections from inside of the docker image, you must expose the cacert Nix package via the relevant environment variable:
{
# Inside dockerTools.buildImage
config = {
Env = [
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
# Ref: https://hackage.haskell.org/package/x509-system-1.6.7/docs/src/System.X509.Unix.html#getSystemCertificateStore
"SYSTEM_CERTIFICATE_PATH=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
];
};
}