Building a python docker image in Nix

Hi, Im stuck with translating a docker image into Nix.

I have this docker Image:

FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install flask
EXPOSE 5000
ENV FLASK_APP=app.py
CMD ["python", "app.py"]

I started by making a nix file like this:

{ pkgs ? import <nixpkgs> { } }:

pkgs.dockerTools.buildImage {
  name = "flask-app";

  contents = [
    pkgs.python312
    pkgs.python312Packages.flask
    ./app.py
  ];

  config = {
    Cmd = [
      "${pkgs.python312}/bin/python app.py"
    ];
    ExposedPorts = [ "5000" ];
  };
}

nix-build fail because of the app.py

I feel like I have searched the whole internet to get there, and I really dont know how to get this thing to work :confused:

I would also like to be able to build the image with the same base image python:3.12-slim.

Any help or working example would be appreciated :slight_smile:

Try this:

{ pkgs ? import <nixpkgs> { } }:
pkgs.dockerTools.buildImage {
  name = "flask-app";
  config = {
    Cmd = [
      (pkgs.lib.getExe (pkgs.python3.withPackages (ps: with ps; [ flask ])))
      ./app.py
    ];
    ExposedPorts = { "5000/tcp" = { }; };
  };
}

I can also recommend using buildLayeredImage function instead.

1 Like

I get an exec /nix/store/rnbknf85mdk1dpd9baabn27lvvgbkzf5-python3-3.12.4-env/bin/python3.12: exec format error when running docker load < /nix/store/.....

(I had to remove the exposedPorts btw)

This probably means you’re building an image for one architecture but running it on a different one.

Ohhh yes, I’m on macos aarch64

Do you think i can use pkgs.pkgsCross.musl64 ?

I will try it

Probably not: Cross compilation — nix.dev documentation

macOS/Darwin is a special case, as not the whole OS is open-source. It’s only possible to cross compile between aarch64-darwin and x86_64-darwin . aarch64-darwin support was recently added, so cross compilation is barely tested.

You’ll need to build on a linux machine

Ok I will try this, thank you for your help !

Here’s a weird trick: create a script nix-in-docker

#!/usr/bin/env bash
set -euo pipefail

if ! command -v docker &> /dev/null
then
  echo "Please install docker first."
  exit 1
fi

if ! docker container inspect nix-docker > /dev/null 2>&1; then
  docker create --privileged --name nix-docker -it -w /work -v $(pwd):/work -v /var/run/docker.sock:/var/run/docker.sock nixos/nix
  docker start nix-docker > /dev/null
  docker exec nix-docker bash -c "git config --global --add safe.directory /work"
  docker exec nix-docker bash -c "echo 'sandbox = true' >> /etc/nix/nix.conf"
  docker exec nix-docker bash -c "echo 'filter-syscalls = false' >> /etc/nix/nix.conf"
  docker exec nix-docker bash -c "echo 'max-jobs = auto' >> /etc/nix/nix.conf"
  docker exec nix-docker bash -c "echo 'experimental-features = nix-command flakes' >> /etc/nix/nix.conf"
  docker exec nix-docker bash -c "nix-env -iA nixpkgs.docker"
fi

docker start nix-docker > /dev/null
docker exec -it nix-docker "$@"

Then you can build for linux in docker using nix :

% cat app.py
print("hello")
% cat test.nix
{ pkgs ? import <nixpkgs> { } }:
pkgs.dockerTools.streamLayeredImage {
  name = "flask-app";
  tag = "latest";
  config = {
    Cmd = [
      (pkgs.lib.getExe (pkgs.python3.withPackages (ps: with ps; [ flask ])))
      ./app.py
    ];
    ExposedPorts = { "5000/tcp" = { }; };
  };
}
% ./nix-in-docker bash -c '$(nix-build --no-out-link ./test.nix) | docker load'
No 'fromImage' provided
Creating layer 1 from paths: ['/nix/store/hv86slhk48azwbsrbhcj96hncmhhlivy-libunistring-1.1']
Creating layer 2 from paths: ['/nix/store/27vkyi25zyg389h30hlycg0d7cnvxdc4-libidn2-2.3.7']
Creating layer 3 from paths: ['/nix/store/lf4aygw01556s2rmc5r6bmvbk2csl1yw-xgcc-13.2.0-libgcc']
Creating layer 4 from paths: ['/nix/store/p852ydpr8zlq0szh5fpvvbzzjaq2ydp5-glibc-2.39-52']
Creating layer 5 from paths: ['/nix/store/6w326q1jsfx0am40pdpypv2vvamb1ga2-ncurses-6.4']
Creating layer 6 from paths: ['/nix/store/x5rjb9waw4rq71dawppzbwh665zz2v4l-zlib-1.3.1']
Creating layer 7 from paths: ['/nix/store/8vhl7vphcrrdbr4vq619z38pw6rldg40-gcc-13.2.0-libgcc']
Creating layer 8 from paths: ['/nix/store/2m7agxcdx1lxxb1j3s0ax08q25gpf42c-bash-5.2p26']
Creating layer 9 from paths: ['/nix/store/8imm5jfrj30a25c3k00w6hgp17kgmzly-gcc-13.2.0-lib']
Creating layer 10 from paths: ['/nix/store/w29ni1swl57igsdnrqd6dz3fg2k8n0bg-bzip2-1.0.8']
Creating layer 11 from paths: ['/nix/store/6slgfzsqii7pgf722hilrxqvrbd5x3gy-expat-2.6.2']
Creating layer 12 from paths: ['/nix/store/61niss1jsj0mji7kh953j9aywv2jqv93-gdbm-1.23']
Creating layer 13 from paths: ['/nix/store/l647q83z7wacl2akm2ismlkx0x9d36a0-libffi-3.4.6']
Creating layer 14 from paths: ['/nix/store/inrx2z41965zc4psk4n9h206hkn1fy3g-libxcrypt-4.4.36']
Creating layer 15 from paths: ['/nix/store/g0vm4hi04x3gjdz64b4a0rkvjii49q1g-mailcap-2.1.53']
Creating layer 16 from paths: ['/nix/store/viwacf92as039w84415shdhxyb69gfn3-mpdecimal-4.0.0']
Creating layer 17 from paths: ['/nix/store/m2c94nnyjhb20jdvb1671rhcrjywcxv4-openssl-3.0.14']
Creating layer 18 from paths: ['/nix/store/npawvy9rws8ifi2dd9c9z56j7lmd9b0f-readline-8.2p10']
Creating layer 19 from paths: ['/nix/store/p7skikz70hkik6w724fxcpvga8n5nccb-sqlite-3.45.3']
Creating layer 20 from paths: ['/nix/store/81rfrad1g90c5fk5s11mkxwnqh1x9ykm-tzdata-2024a']
Creating layer 21 from paths: ['/nix/store/4fanz2da3w8dcf41ji8g8x1yqd1dqx4w-xz-5.4.7']
Creating layer 22 from paths: ['/nix/store/1rnzwz9b7912c2dng6gswiaz0gwk8w7x-python3-3.11.9']
Creating layer 23 from paths: ['/nix/store/gpiwkk0dbvyhmvbskjxzljhncmqx6ay8-python3.11-markupsafe-2.1.5']
Creating layer 24 from paths: ['/nix/store/5crnmmvm1132hnril3js5rjx9ifkfhja-python3.11-blinker-1.7.0']
Creating layer 25 from paths: ['/nix/store/mpa371bynwna3fnivxfgrdhz5nyz793f-python3.11-click-8.1.7']
Creating layer 26 from paths: ['/nix/store/ck6b1fb2f765xbjazckrhwxzpfg7x9xv-python3.11-itsdangerous-2.2.0']
Creating layer 27 from paths: ['/nix/store/g72yf6r9q99qp8iny7fjgjs4a2sfnsz7-python3.11-jinja2-3.1.4']
Creating layer 28 from paths: ['/nix/store/ziklj6wlgx6s7xycha992z13mr87xbrj-python3.11-werkzeug-3.0.3']
Creating layer 29 from paths: ['/nix/store/zliqz38i91dsrffgxp6v301k8s0n7j60-python3.11-flask-3.0.3']
Creating layer 30 from paths: ['/nix/store/djgq94bi0ixcn8dhdrlx8qk70rm245m4-app.py']
Creating layer 31 from paths: ['/nix/store/ggkc81v0xs7q967xq1kd0ic2jr480xr7-python3-3.11.9-env']
Creating layer 32 with customisation...
Adding manifests...
Done.
Loaded image: flask-app:latest
% docker run --rm -it flask-app:latest
hello
2 Likes

Using docker to build the docker image ? lmao

1 Like