NixOps: rebuilding everything?

I have a server.nix file which is used by nixops. In that file there is app = (import app.nix. {}). When I change something in server.nix without changing anything in app.nix, app.nix still gets rebuilt. Is this how it should be? Shouldn’t nixops see that app.nix derivation already exists with the same hash and simply use the existing stuff from the store?
If I had built app.nix with nix-build --no-out-link option then the following nix-build would not have rebuilt anything as nothing had changed.

1 Like

It would be good if you could share a minimal example of the code that causes your issue.

1 Like
# server.nix
{
  network.enableRollback = true;

  webserver = { config, pkgs, ... }:
    let app = (import ./default.nix { });
    in {

      services.postgresql = {
        enable = true;
        package = pkgs.postgresql_13;
        enableTCPIP = true;
      };
      services.postgresqlBackup.enable = true;
      environment.systemPackages = with pkgs; [ htop wget emacs-nox ];
      security.acme.email = "example@email.com";
      security.acme.acceptTerms = true;
      networking.firewall.allowedTCPPorts = [ 80 443 5432 ];
      services.nginx = {
        enable = true;
        recommendedGzipSettings = true;
        recommendedOptimisation = true;

        virtualHosts."my-example-node-app.com" = {
          forceSSL = true;
          enableACME = true;
          locations."/" = { proxyPass = "http://127.0.0.1:3000"; };
        };

        virtualHosts."example-nix-manual.com" = {
          forceSSL = true;
          enableACME = true;
          locations = {
            "/" = {
              root =
                "${config.system.build.manual.manualHTML}/share/doc/nixos/";
            };
          };
        };
      };

      systemd.services.my-example-node-app = {
        enable = true;
        serviceConfig = {
          WorkingDirectory = "${app}";
          # run a nodejs server on port 3000
          ExecStart = "${app}/server-bin";
        };
        wantedBy = [ "multi-user.target" ];
      };
    };
}

Hi. I use this server.nix file with nixops (nixops create -d server-on-vbox server.nix vbox.nix and then the deploy command).

The file called default.nix contains building instructions of a nodejs project. If I only did nix-build default.nix --no-out-link twice without changing a source file in my node’s project, the second build would finish without doing any building again as nothing has changed and it would take an already built derivation from the nix store.
However, when I only change something in server.nix file and do not change anything in my nodejs project and do nixops deploy -d server-on-vbox, it rebuilds the whole nodejs project again.

It looks like your default.nix file is in the same directory, so my first guess is that it is using the directory’s contents as a source and changing this file changes the source. Perhaps you could look into filtering the source.

2 Likes
# default.nix
  src = builtins.filterSource (path: _:
    baseNameOf path != "server.nix" && baseNameOf path != "vbox.nix") ./.;

This did what I was hoping for :slight_smile: . Thanks @ryantm

1 Like

Glad you got it working. Please see also: Best practices — nix.dev documentation

I’ve tried to apply builtins.path function as follows

  let filteredSrc = builtins.filterSource (path: _:
    baseNameOf path != "server.nix" && baseNameOf path != "vbox.nix")
    (builtins.path {
      path = ./.;
      name = "example-proj";
    });

Unfortunately, this fails with this error: while evaluating the attribute ‘src’ of the derivation … cannot refer to other paths, at …

Not sure about the error you are getting but you can probably get rid of filterSource, since you can specify a filter inside the set passed to the builtins.path. Like:

let filteredSrc = builtins.path {
  path = ./.;
  filter = path: _: baseNameOf path != "server.nix" && baseNameOf path != "vbox.nix";
  name = "example-proj";
};

See builtins.path under Introduction - Nix Reference Manual

1 Like

Actually I’ve just tried what @ilkecan has suggested but found out that builtins.path returns the path of the nix store path where the path, given as an argument, will be copied to, whether builtins.filterSource returns the actual working directory path.

Using builtins.path with filter attribute rebuilds the project when changing server.nix contents. Shouldn’t that work too as we filter that file? What causes nix to generate two different store paths in this case?

That sentence is a bit long and I am having a trouble understanding it. Can you explain what you mean?

Did you remove the name attribute from the set passed to builtins.path? Because if you removed it, the name of the directory will depend on the unfiltered one and the name of the unfiltered one depends on all the files including “server.nix”. If you didn’t remove the name attribute I have no idea why it rebuilds.