Easy IHaskell Docker Images with Nix

Posted on 11 August 2019

Today I learned how to turn an IHaskell Nix expression into a Docker image. Here is an example:

# default.nix let pkgs = { = { ihaskell = builtins.fetchTarball { = builtins.fetchTarball { url = "https://github.com/gibiansky/IHaskell/tarball/93bfa3a7a434c1dfe6873c2105c43856c282e183" ; sha256 = "1cvqqmpvz7s3d7zclmkm5igx36clrbdiafs47i9rik3rdzw0gr3d" ; }; nixpkgs = builtins.fetchTarball { = builtins.fetchTarball { url = "https://github.com/NixOS/nixpkgs-channels/tarball/9ca57dc9171ca4547abf076a8987ed73c46f2e15" ; sha256 = "18d01cw6s6k9fnac3vq0k6inybqalkz4ak88pw67q4wqzq9rc07l" ; }; }; nixpkgs = import pkgs.nixpkgs {}; = import pkgs.nixpkgs {}; NB_USER = "jovyan" ; NB_UID = "1000" ; dockerEtc = nixpkgs.runCommand "docker-etc" {} '' = nixpkgs.runCommand{} mkdir -p $out /etc/pam.d -p/etc/pam.d echo "root:x:0:0::/root:/bin/sh" > $out /etc/passwd /etc/passwd echo " ${NB_USER} :x:1000:1000::/home/ ${NB_USER} :" >> $out /etc/passwd /etc/passwd echo "root:!x:::::::" > $out /etc/shadow /etc/shadow echo " ${NB_USER} :!:::::::" >> $out /etc/shadow /etc/shadow echo "root:x:0:" > $out /etc/group /etc/group echo " ${NB_USER} :x:1000:" >> $out /etc/group /etc/group echo "root:x::" > $out /etc/gshadow /etc/gshadow echo " ${NB_USER} :!::" >> $out /etc/gshadow /etc/gshadow '' ; ihaskell = import " ${pkgs .ihaskell } /release.nix" { = import inherit nixpkgs ; nixpkgs compiler = "ghc864" ; packages = self: with self ; []; = self: with self[]; }; in nixpkgs.dockerTools.buildLayeredImage { name = "ihaskell-nix" ; tag = "latest" ; contents = [ = [ dockerEtc ihaskell nixpkgs.bashInteractive ]; config = { = { Cmd = [ "ihaskell-notebook" "--ip" "0.0.0.0" ] ; = [ User = NB_USER ; = NB_USER WorkingDir = "/home/ ${NB_USER} " ; }; extraCommands = '' mkdir -m 1777 ./tmp -m 1777 ./tmp mkdir -m 777 -p ./home/ ${NB_USER} -m 777 -p ./home/ '' ; maxLayers = 100 ; = 100 };

This is how to use it:

$ docker load < $( nix-build default.nix ) loaddefault.nix $ docker run -p8888:8888 -it ihaskell-nix:latest run -p8888:8888 -it ihaskell-nix:latest

This uses IHaskell’s built-in release.nix to do most of the heavy lifting for IHaskell itself, and does a couple of other things:

Creates the /tmp directory Sets up a jovyan user, because Jupyter complains when run as root Includes bash , which is not strictly necessary but is useful for poking around in the image and for using :! from within a notebook

Building the image and loading it into Docker are both very slow compared to using Nix directly (even though I’m using the Nix support for layered images), so I wouldn’t recommend using this approach for local development. I’m primarily interested in doing this to:

Share IHaskell notebooks with people who are less comfortable with Nix Deploy to platforms such as Amazon’s Elastic Container Service and Google’s App Engine, which offer excellent support for Docker and no support for Nix

Unfortunately this isn’t quite ready to deploy yet, especially because Jupyter uses token-based authentication by default and the console output will not necessarily be available after deployment. It’s possible to set a password instead, so I expect that copying the output of jupyter notebook --generate-config and changing the relevant settings will be enough. I hope to post instructions when I get around to trying this myself.

In the meantime, I hope this is useful as a way of making IHaskell even more widely available, and as a demonstration of using dockerTools to bridge the gap between Nix and Docker!