Massive docker image when building rust using cargo2nix

I am trying to cross compile a rust project to a docker image using cargo2nix. However the docker image seems to pull in all the build tools when I only relay want the static binary this results in rather large docker images.

{
  description = "Cross-compiling nexus_s3";
  inputs = {
    cargo2nix.url = "github:cargo2nix/cargo2nix/main";
    flake-utils.follows = "cargo2nix/flake-utils";
    nixpkgs.follows = "cargo2nix/nixpkgs";
  };
  outputs = inputs:
    with inputs;
      flake-utils.lib.eachDefaultSystem (
        system: let
          pkgs = import nixpkgs {
            inherit system;
            #           crossSystem.config = "aarch64-unknown-linux-musl";
            crossSystem.config = "x86_64-unknown-linux-musl";
            overlays = [cargo2nix.overlays.default];
          };

          appName = "nexus_s3";

          # create the workspace & dependencies package set
          rustPkgs = pkgs.rustBuilder.makePackageSet {
            rustVersion = "1.85.0";
            packageFun = import ./Cargo.nix;
          };
        in rec {
          packages = {
            cross-package = rustPkgs.workspace.nexus_s3 {};
            docker  = pkgs.dockerTools.buildLayeredImage {
              name = appName;
              config.Cmd = "${packages.cross-package}/bin/${appName}";
            };
          };
        }
      );
}

When I build the package with nix build .#cross-package i get a binary that is 17Mb.
When building with nix build .#docker i get an tar image that is 498Mb.

After inspecting the image I see that there all the rust build libs, gcc etc have been included is there an idiomatic way to exclude these and just keep the binary?

In theory nix will only include packages in the package closure that contain paths that exist as literal strings somewhere in the output. This implies that your binary (or any other files in the build output) contains references to all these development libraries; you should check what it’s linked against with ldd and make sure it’s an actual release build.

While I’m at it, you should probably use the contents attr instead of setting the Cmd to the binary path, and then use /bin/<command>. Also, instead of rec, use self.packages.${system}, it’s much clearer, less prone to confusing questions like “which rec has precedence?” and makes static analysis easier. Also, whoa, do I see with inputs there? Flake outputs can be functions, just use { self, nixpkgs, cargo2nix, flake-utils, ... }: and save yourself the scoping confusion and make your indentation cleaner. I’ve never seen with abused to this extent, this is like a masterclass in antipatterns :smiley: