Delete nativeBuildInputs from resulting image?

I am trying to migrate my container infrastructure (both CI and production) to built-by-nix images.
I successfully managed to build image using dockerTools.buildImage. But I found out that this image is two times bigger than the one crafted with alpine linux. After inspecting the image I found out that it contains the whole llvm package in it. Indeed, dependency of the package I specify in buildImage.contents has nativeBuildInputs = [ llvm_8 ] but it’s there only for this dependency to static link with it, there is no need for the whole llvm distro neither in the llvm-lib in the docker image:

❯ du -d 3 -h image-unpacked/
... ommited ...
200M    image-unpacked/nix/store/fr8zb33nfgqwxcwivy6g6949yh1gb9f1-llvm-8.0.1
81M     image-unpacked/nix/store/xm6cvgw88cj4yf69i0zdqlykchanrlfx-llvm-8.0.1-lib

I understand that I can modify docker derivation to manually extract binaries from my app package but I wonder if there is any way to get rid from unneeded nativeBuildInputs in the first place, because I suspect llvm won’t be garbage collected until the package with transitive nativeBuildInputs containing it won’t installed.
My current docker derivation:

        myservice = pkgs.callPackage ./nix/myservice.nix {};
        myservice-docker =
          pkgs.dockerTools.buildImage {
            name = "myservice";
            contents = [ myservice ];
            tag = "latest";
            config = {
              Cmd = [ "/bin/myservice" ];
              Expose = 3333;
            };
          };
1 Like

Can you clarify this? I don’t see llvm_8 in the code you gave so I don’t really understand what this mean.

nativeBuildInputs = [ llvm_8 ] are present in one of myservice's deps.
Myservice is nothing more than a derivation generated by crate2nix with nativeBuildInputs overriden to satisfy linker:

{ pkgs ? import ./nixpkgs.nix }:

let
  cargoNix = pkgs.callPackage ./Cargo.nix { };
  myservice = cargoNix.workspaceMembers.myservice.build.override {
    crateOverrides = pkgs.defaultCrateOverrides // {
      llvm-sys = attrs: with pkgs; {
        nativeBuildInputs = [ llvm_8 ];
      };
      prost-build = attrs: with pkgs; {
        PROTOC = "${protobuf}/bin/protoc";
      };
      prosto = attrs: with pkgs; {
        nativeBuildInputs = [ protobuf rustfmt ];
        PROTOC = "${protobuf}/bin/protoc";
      };
      myservice = attrs: with pkgs; {
        nativeBuildInputs = [ pkgconfig ncurses5 libffi libxml2 ];
      };
    };
  };
in myservice

Curiously, there is no protobuf in the resulting image’s nix store, but there is ncurses, libffi, libxml2, llvm-8.0.1 and llvm-8.0.1-lib.

protobuf makes sense, as it’s likely only used to make the language bindings, and then nix wont find any references to it’s store path, so it doesnt end up in the result.

for the others, you need to check the result outputs to see where there’s store paths to those packages. Sometimes it’s debug info, sometimes it’s some configuration files which will have their store path, and then nix determines that it needs to include those paths in the closure.

1 Like

Indeed, grep shows that binary myservice matches llvm store path but ldd executed against myservice binary does not show llvm (but shows libxml, libffi, etc which is expected since they aren’t static linked). readelf -a myservice also doesn’t list llvm store.
Running grep against strip-ed binary also matches llvm store path, both llvm-8.0.1 and llvm-8.0.1-lib.
I’ll get back when I dig more info.

Found reference to a header file via hexdump:

01ff05b0  74 70 74 72 5f 74 29 73  65 63 74 69 6f 6e 2e 62  |tptr_t)section.b|
01ff05c0  61 73 65 20 2b 20 73 65  63 74 69 6f 6e 2e 73 69  |ase + section.si|
01ff05d0  7a 65 00 00 00 00 00 00  76 6f 69 64 20 6c 6c 76  |ze......void llv|
01ff05e0  6d 3a 3a 52 75 6e 74 69  6d 65 44 79 6c 64 3a 3a  |m::RuntimeDyld::|
01ff05f0  73 65 74 50 72 6f 63 65  73 73 41 6c 6c 53 65 63  |setProcessAllSec|
01ff0600  74 69 6f 6e 73 28 62 6f  6f 6c 29 00 00 00 00 00  |tions(bool).....|
01ff0610  2f 6e 69 78 2f 73 74 6f  72 65 2f 66 72 38 7a 62  |/nix/store/fr8zb|
01ff0620  33 33 6e 66 67 71 77 78  63 77 69 76 79 36 67 36  |33nfgqwxcwivy6g6|
01ff0630  39 34 39 79 68 31 67 62  39 66 31 2d 6c 6c 76 6d  |949yh1gb9f1-llvm|
01ff0640  2d 38 2e 30 2e 31 2f 69  6e 63 6c 75 64 65 2f 6c  |-8.0.1/include/l|
01ff0650  6c 76 6d 2f 45 78 65 63  75 74 69 6f 6e 45 6e 67  |lvm/ExecutionEng|
01ff0660  69 6e 65 2f 52 75 6e 74  69 6d 65 44 79 6c 64 2e  |ine/RuntimeDyld.|
01ff0670  68 00 00 00 00 00 00 00  21 44 79 6c 64 20 26 26  |h.......!Dyld &&|
01ff0680  20 22 73 65 74 50 72 6f  63 65 73 73 41 6c 6c 53  | "setProcessAllS|
01ff0690  65 63 74 69 6f 6e 73 20  6d 75 73 74 20 62 65 20  |ections must be |
01ff06a0  63 61 6c 6c 65 64 20 62  65 66 6f 72 65 20 6c 6f  |called before lo|
01ff06b0  61 64 4f 62 6a 65 63 74  2e 22 00 00 00 00 00 00  |adObject."......|
01ff06c0  31 33 4d 65 6d 6f 72 79  4d 61 6e 61 67 65 72 00  |13MemoryManager.|

That explains my problem. Thanks.

1 Like