Compile NixOS derivations into a single file for a single host

Thank you so much the the great write up. I’ll test it today!

Strangely I failed on this step. Also nix copy $configDrv --to file:///tmp/configCache copied thousands of xxx.narinfo files and the full size of the directory was over 3 GiB. I’ll try again this evening.

Thank you.

What do you mean by that? What was the error message?

Hmm, could it be that you’re using some software that has its whole binary distribution as the input? Most unfree software does this, as well as packages ending in -bin.

Apologize for the delay. I tested it again today:

  • NixOS EC2
  • very simple configuration
cat configuration.nix
{ modulesPath, pkgs, ... }: {
  imports = [ "${modulesPath}/virtualisation/amazon-image.nix" ];

  environment.systemPackages = with pkgs; [ htop vim tmux tree bottom ];

  nix =
    {
      settings =
        {
          experimental-features = [ "nix-command" "flakes" ];
          warn-dirty = false;
          auto-optimise-store = true;
        };
    };
}
nix --extra-experimental-features flakes --extra-experimental-features nix-command flake show
nixos-rebuild dry-build --flake /etc/nixos#mynix |& grep /nix/store | wc -l
601
nixos-rebuild build --flake /etc/nixos#mynix
readlink result
/nix/store/070ybkrrnzhh7mzv8m6r013s6yr9g247-nixos-system-unnamed-23.05.20230829.2ab91c8
nix-store -q --deriver result
/nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv
nix --extra-experimental-features flakes --extra-experimental-features nix-command copy /nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv --to file:///tmp/mynix
tar -czf /tmp/mynix.tar.gz /tmp/mynix
du -sh /tmp/mynix*
291M    /tmp/mynix
287M    /tmp/mynix.tar.gz
  • new blank NixOS EC2
du -sh /tmp/mynix*
291M    /tmp/mynix
287M    /tmp/mynix.tar.gz
nix --extra-experimental-features flakes --extra-experimental-features nix-command store ls --store file:///tmp/nixos /nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv
error: path '/nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv' is not a valid store path
nix --extra-experimental-features flakes --extra-experimental-features nix-command copy /nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv --from file:///tmp/nixos
error: cannot build missing derivation '/nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv'

Based on what you’ve shown, it like you should be using file:///tmp/mynix instead of file:///tmp/nixos on the EC2 instance.

Thank you. That was copy/paste issue. I’ve just tried again:

[root@ip-10-0-0-51:/tmp]# du -sh /tmp/mynix*
291M    /tmp/mynix
287M    /tmp/mynix.tar.gz

[root@ip-10-0-0-51:/tmp]# nix --extra-experimental-features flakes --extra-experimental-features nix-command store ls --store file:///tmp/mynix /nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv
error: path '/nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv' is not a valid store path

[root@ip-10-0-0-51:/tmp]# nix --extra-experimental-features flakes --extra-experimental-features nix-command copy /nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv --from file:///tmp/mynix
error: cannot build missing derivation '/nix/store/rn6brlsf6z80ffhgqdkf63b8020hl0hd-nixos-system-unnamed-23.05.20230829.2ab91c8.drv'

Thank you @iFreilicht for the instructions. Unfortunately I get the same error as @mark.c . Is it possible that the instructions work only when there is a very small difference between the source (builder) and the target?

I’ve just tried it again and I’m sorry, it doesn’t work.

[root@ip-10-0-0-94:/etc/nixos]# cat configuration.nix flake.nix
{ modulesPath, pkgs, ... }: {
  imports = [ "${modulesPath}/virtualisation/amazon-image.nix" ];

  environment.systemPackages = with pkgs; [ htop vim tmux tree bottom ];

  nix =
    {
      settings =
        {
          experimental-features = [ "nix-command" "flakes" ];
          warn-dirty = false;
          auto-optimise-store = true;
        };
    };
}


{
  inputs =
    {
      nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
    };

  outputs = { self, nixpkgs }:
    let
      system = "x86_64-linux";
      pkgs =
        import nixpkgs
          {
            inherit system;
          };
      lib = nixpkgs.lib;
    in
      {
        nixosConfigurations =
          {
            abc = lib.nixosSystem
              {
                inherit system;
                modules = [ ./configuration.nix ];
              };
          };
      };
}

[root@ip-10-0-0-94:/etc/nixos]# cd

[root@ip-10-0-0-94:~]# nixos-rebuild build --flake /etc/nixos#abc
building the system configuration...
warning: creating lock file '/etc/nixos/flake.lock'
trace: warning: system.stateVersion is not set, defaulting to 23.05. Read why this matters on https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion.

[root@ip-10-0-0-94:~]#

[root@ip-10-0-0-94:~]# readlink result
/nix/store/379k42cmcaaklb8mgd3xv4f23dfi4wg2-nixos-system-unnamed-23.05.20230907.4f77ea6

[root@ip-10-0-0-94:~]# nix-store -q --deriver result
/nix/store/5k836ssrihmrcmmx8m8z74fbkvf1f3h6-nixos-system-unnamed-23.05.20230907.4f77ea6.drv

[root@ip-10-0-0-94:~]# nix --extra-experimental-features flakes --extra-experimental-features nix-command copy /nix/store/5k836ssrihmrcmmx8m8z74fbkvf1f3h6-nixos-system-unnamed-23.05.20230907.4f77ea6.drv --to file:///tmp/abc

[root@ip-10-0-0-94:~]# tar -czf /tmp/abc.tar.gz /tmp/abc/
tar: Removing leading `/' from member names

[root@ip-10-0-0-94:~]# du -sh /tmp/abc*
283M    /tmp/abc
279M    /tmp/abc.tar.gz

[root@ip-10-0-0-94:~]#
  • another EC2
[root@ip-10-0-0-219:~]# du -sh /tmp/abc*
283M    /tmp/abc
279M    /tmp/abc.tar.gz

[root@ip-10-0-0-219:~]# nix --extra-experimental-features flakes --extra-experimental-features nix-command copy /nix/store/5k836ssrihmrcmmx8m8z74fbkvf1f3h6-nixos-system-unnamed-23.05.20230907.4f77ea6.drv --from file:///tmp/abc
error: cannot build missing derivation '/nix/store/5k836ssrihmrcmmx8m8z74fbkvf1f3h6-nixos-system-unnamed-23.05.20230907.4f77ea6.drv'

[root@ip-10-0-0-219:~]#

Could you please review your instructions @iFreilicht ?

Thank you :wink:

Please refer to the private message I sent you. I will have a look in the week after 16th of September.

You may already know this, but you can build on one machine and push to another by just adding “–target-host (ssh-username)@(host.domain)” to the nixos-rebuild command.

Of course, this requires that the user has the ability to execute sudo for the necessary commands. In my case I have set my root user to use “*” as the password hash so that I can use a .ssh key to log into it.

This gives the push/pull functionality that nix-ops provides without setting up nix-ops. My small experience with nix-ops is that it also gives more real-time status of every system it is connected to.

Hi @genson ,

We discussed this option earlier but I totally understand that you didn’t read every single post - the thread is very long…

The idea and my need is to take a flake repo with a single or multiple NixOS configurations and somehow boil it down to a single (small) file that will target a single host and won’t contain any comments or functions or mkIf etc.

When I opened this thread a few months ago I foolishly assumed that this is a simple thing to do since nix probably has something like a compilation phase / stage when all mkIf and other things are converted to the actual values, etc. But it turned out this is much more complicated :wink:

1 Like

I am a little suspicious of this line in your flake. This might be what causes the size of the cache to baloon to over 200MB. I don’t know what’s in that module, so hard to say, but all the other tools are definitely very simple.

Ok, that’s pretty bad :confused:

Does the second command (nix store ls ...) successfully return the derivation on the machine you built the configuration on?

I just started renting a small VPC on Hetzner and will try these steps, including the transfer, from my home machine to that VPC later.

Either way, it is pretty annoying that --from doesn’t fail with a proper error if the passed directory doesn’t even exist.

2 Likes

Ok so I tried it once more now. I followed everything exactly as I described, and it all worked.
I have three machines:

  • horse, my current laptop
  • junction, my home server
  • gateway, my Hetzner VPS

The logs below are literally verbatim copied from me following the instructions. I only changed the
way I set $configDrv to make mistakes less likely and omitted some details with [...].
You can see the full NixOS configurations and the flake used to build them in
my dotfiles repo on github

felix@horse:~$ ssh junction
Last login: Fri Aug 25 22:21:35 2023 from [...]
felix@junction:~$ nixos-rebuild build --flake path:/home/felix/.dotfiles
building the system configuration...
felix@junction:~$ readlink result
/nix/store/40jck40wcbjh4xhdpm301fk45f1vky8b-nixos-system-junction-23.11.20230812.f045184
felix@junction:~$ configDrv=$(nix-store -q --deriver result)
felix@junction:~$ echo "$configDrv"
/nix/store/cm8r7al3q69ys5dy7hg4f962973j8dz6-nixos-system-junction-23.11.20230812.f045184.drv
felix@junction:~$ nix copy $configDrv --to file:///tmp/configCache
felix@junction:~$ cd /tmp
felix@junction:/tmp$ du -sh configCache
21M     configCache
felix@junction:/tmp$ tar -czvf config.tar.gz configCache
configCache/
configCache/v7ihfx32zv7bdha6i9dd6a3r0knzs8j3.narinfo
[...]
configCache/5qynqmq6rd2ahaf4760bj2b2ksqg80hy.narinfo
configCache/vq1bx1j10363hnl2by0fcchm7a8nl3ha.narinfo
felix@junction:/tmp$ du -sh config.tar.gz
5.6M    config.tar.gz
felix@junction:/tmp$ nix store ls --store file:///tmp/configCache $configDrv
cm8r7al3q69ys5dy7hg4f962973j8dz6-nixos-system-junction-23.11.20230812.f045184.drv
felix@junction:/tmp$ exit
Connection to junction closed.
felix@horse:~$ scp junction:/tmp/config.tar.gz gateway:/tmp
felix@horse:~$ ssh gateway
Last login: Fri Aug 25 22:12:32 2023 from [...]
felix@gateway:~$ cd /tmp
felix@gateway:/tmp$ tar -xvzf config.tar.gz
configCache/
configCache/v7ihfx32zv7bdha6i9dd6a3r0knzs8j3.narinfo
[...]
configCache/5qynqmq6rd2ahaf4760bj2b2ksqg80hy.narinfo
configCache/vq1bx1j10363hnl2by0fcchm7a8nl3ha.narinfo
felix@gateway:/tmp$ configDrv=/nix/store/cm8r7al3q69ys5dy7hg4f962973j8dz6-nixos-system-junction-23.11.20230812.f045184.drv
felix@gateway:/tmp$ nix copy $configDrv --from file:///tmp/configCache
warning: The interpretation of store paths arguments ending in `.drv` recently changed. If this command is now failing try again with '/nix/store/cm8r7al3q69ys5dy7hg4f962973j8dz6-nixos-system-junction-23.11.20230812.f045184.drv^*'
felix@gateway:/tmp$ nix build "${configDrv}^*"
felix@gateway:/tmp$ nix store ls --store file:///tmp/configCache $configDrv
cm8r7al3q69ys5dy7hg4f962973j8dz6-nixos-system-junction-23.11.20230812.f045184.drv

I’m really puzzled now, the process itself absolutely works. It seems to be properly implemented in Nix itself, its unlikely the culprit here.

Again, the amazon-image.nix import seems to me like a likely contributor to the huge size of the cache nix copy created, but I can’t say for sure. Maybe I’ll have time soon to rent an EC2 instance myself and try it there as well.

Can you try this without importing amazon-image.nix? Or can you maybe share that file somehow?

2 Likes

Ah, one other thing that I’m noticing just now is your invocation of tar:

tar -czf /tmp/abc.tar.gz /tmp/abc/

This causes the archive to be nested, so you might have to use --from file:///tmp/abc/abc when running nix copy.

1 Like

Ouh! I need to tar -czf /tmp/abc.tar.gz -C /tmp abc

I’ll try again! Thank you!

The amazon-image file is just:

2 Likes
  • first EC2
ssh firstnix
cd /etc/nixos
cat configuration.nix flake.nix
{ modulesPath, pkgs, ... }: {
  imports = [ "${modulesPath}/virtualisation/amazon-image.nix" ];

  environment.systemPackages = with pkgs; [ htop vim tmux tree bottom ];

  nix =
    {
      settings =
        {
          experimental-features = [ "nix-command" "flakes" ];
          warn-dirty = false;
          auto-optimise-store = true;
        };
    };
}
{
  inputs =
    {
      nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
    };

  outputs = { self, nixpkgs }:
    let
      system = "x86_64-linux";
      pkgs =
        import nixpkgs
          {
            inherit system;
          };
      lib = nixpkgs.lib;
    in
      {
        nixosConfigurations =
          {
            abc = lib.nixosSystem
              {
                inherit system;
                modules = [ ./configuration.nix ];
              };
          };
      };
}
nix build --no-link --print-out-paths /etc/nixos#nixosConfigurations.abc.config.system.build.toplevel
/nix/store/s38ck7liswgiskxh8smccywzkkdrixzl-nixos-system-unnamed-23.05.20231011.bd1cde4
nix-store -q --deriver /nix/store/s38ck7liswgiskxh8smccywzkkdrixzl-nixos-system-unnamed-23.05.20231011.bd1cde4
/nix/store/dmyyml7l7kbbcs6jvnckqrk5wldaq1f1-nixos-system-unnamed-23.05.20231011.bd1cde4.drv
nix copy /nix/store/dmyyml7l7kbbcs6jvnckqrk5wldaq1f1-nixos-system-unnamed-23.05.20231011.bd1cde4.drv --to file:///tmp/abc
tar -czf /tmp/abc.tar.gz -C /tmp abc
du -sh /tmp/abc.tar.gz /tmp/abc
288M    /tmp/abc.tar.gz
291M    /tmp/abc
  • second EC2
ssh secondnix
tar -xf /tmp/abc.tar.gz -C /tmp
ls -l /tmp/abc | grep narinfo | wc -l
583
ls -l /tmp/abc/nar | grep nar.xz | wc -l
565
nix copy /nix/store/dmyyml7l7kbbcs6jvnckqrk5wldaq1f1-nixos-system-unnamed-23.05.20231011.bd1cde4.drv --from file:///tmp/abc
error: cannot build missing derivation '/nix/store/dmyyml7l7kbbcs6jvnckqrk5wldaq1f1-nixos-system-unnamed-23.05.20231011.bd1cde4.drv'

Weird, I’ll try again tomorrow.

Maybe to make absolutely sure, run only and exactly the commands that I did, including the cd into /tmp? Either way, something about your flake makes it huge, I’ll try to build it myself this weekend and see if I can replicate this.

Absolutely.

ssh first
mkdir -p ~/.config/nix
tee ~/.config/nix/nix.conf <<< 'experimental-features = nix-command flakes'
nix run nixpkgs#vim -- /etc/nixos/configuration.nix
nix run nixpkgs#vim -- /etc/nixos/flake.nix
nixos-rebuild build --flake /etc/nixos#abc
readlink result

/nix/store/s38ck7liswgiskxh8smccywzkkdrixzl-nixos-system-unnamed-23.05.20231011.bd1cde4

configDrv=$(nix-store -q --deriver result)
echo "$configDrv"

/nix/store/dmyyml7l7kbbcs6jvnckqrk5wldaq1f1-nixos-system-unnamed-23.05.20231011.bd1cde4.drv

nix copy $configDrv --to file:///tmp/configCache
cd /tmp
du -sh configCache

291M    configCache

tar -czvf config.tar.gz configCache
du -sh config.tar.gz

288M    config.tar.gz

nix store ls --store file:///tmp/configCache $configDrv

error: path '/nix/store/dmyyml7l7kbbcs6jvnckqrk5wldaq1f1-nixos-system-unnamed-23.05.20231011.bd1cde4.drv' is not a valid store path

@mark.c You’re not going to believe this.

It still worked, and I built the exact same configuration as you:

nixos-rebuild build --flake .#abc
readlink result

/nix/store/s38ck7liswgiskxh8smccywzkkdrixzl-nixos-system-unnamed-23.05.20231011.bd1cde4

configDrv=$(nix-store -q --deriver result)
echo "$configDrv"

/nix/store/dmyyml7l7kbbcs6jvnckqrk5wldaq1f1-nixos-system-unnamed-23.05.20231011.bd1cde4.drv

nix copy $configDrv --to file:///tmp/configCache
cd /tmp
du -sh configCache
25M     configCache

tar -czvf config.tar.gz configCache
du -sh config.tar.gz

6.4M     config.tar.gz

nix store ls --store file:///tmp/configCache $configDrv

dmyyml7l7kbbcs6jvnckqrk5wldaq1f1-nixos-system-unnamed-23.05.20231011.bd1cde4.drv

What the hell is going on here? The derivation was perfectly reproduced on my machine, the hash proves it. Why is your version of nix behaving so differently? Not only does it work, but nix copy produced a much smaller cache on my machine than on yours.

If you run nix --version, what does it return?
Also, what does realpath $(which nix) return?

Maybe this behavior is a bug in a newer or older version of nix?

2 Likes