Can I reuse nixosConfigurations.foo in nixops?

I’m pretty happy with my flake-based config that works on several machines and conveniently shares much of their configs.

I wanted to learn more about nixops as an alternative to pulling the git repo on each machine and nixos-rebuild switching.

I thought I would be able to just reuse an existing configuration, but I can’t seem to get it right.

Things I’ve tried:

nixopsConfigurations.default = {
  nixpkgs = nixpkgs-stable;
  network = {
    storage.legacy = { };
  };
  myMachine = nixosConfigurations.myMachine // {
    deployment.targetHost = "12.34.56.78";
  };
}

Replace nixosConfigurations.myMachine above with various permutations of:

  • nixosConfigurations.myMachine.config.system.build.toplevel
  • nixosConfigurations.myMachine.config.system
  • nixosConfigurations.myMachine._module
  • nixosConfigurations.myMachine._module.args
  •   { ... }: {
      _module.args = {   
          config = nixosConfigurations.myMachine.config;
      };
      deployment.targetHost = "12.34.56.78";
       }
    

A few other permutations. Obviously these are all not working.

Is there an obvious approach that I’m missing here, in order to define a nixops deployment based on an existing nixosSystem?

Relevant links:

There’s a confusing distinction between either

  • a non-concrete configuration, also known as a module (or profile sometimes), which is just a lambda ({ lib, ... }: etc) or an attribute set with values for options.
  • or, a fully instantiated, concrete nixosConfigurations.${x}, constructed with nixosSystem

You can go from the prior to the latter, but not back.

NixOps is a dynamic deployment tool in the sense that some configuration isn’t statically known, or at least in general it is not, as it supports the creation of new machines whose statically unknown metadata can be accessed in the configuration. Crucially, NixOps will call an equivalent of nixosSystem for you with extra modules.

nixos-rebuild on the other hand is a deployment tool that needs one of these fully concrete configurations. It will not call nixosSystem for you.

It used to be that nixos-rebuild worked on modules instead of already-nixosSystem-ed configurations, so this is still a bit of a new phenomenon between NixOS and NixOps.

Modules are still as reusable as they ever were, so if you really want to use both nixops and nixos-rebuild, you could achieve that by moving your configuration into nixosModules first, refactoring it into a structure as I’ve suggested here.

A simpler setup perhaps is to use just one tool. nixos-rebuild is arguably the simpler one, but it can do remote deployments too! Use nixos-rebuild --target-host some-ssh-host.

If you’re allocating machines or using other cloud resources, I’d recommend to stick to nixops.

1 Like

This was what I needed to hear / know. Thank you!

I’d read that a few times, but it didn’t really click until the more in-depth explanation you provided here. Now it makes sense.

Certainly simpler, but at this stage in my nix journey I’m exploring the options available :slight_smile:

With an improved mental model I was able to come up with a satisfactory configuration that is pretty DRY, allows me to reuse my existing infrastructure for local machines, and also have a nixops-able configuration for one remote machine:

The gist of it is:

let 
...
hosts = rec {
    myLocalMachine.system = "x86_64-linux";
    myRemoteMachine.system = "x86_64-linux";
    myLocalDarwinMachine.system = "aarch64-darwin";
}
match_system = regex: (nixpkgs-stable.lib.filterAttrs (_: host: with host; builtins.match regex system != null) hosts);
...
in {
...
nixosModules =
      builtins.mapAttrs
      (hostname: attrs: {inputs, ...}: {
        imports = [
          ({
            config,
            pkgs,
            ...
          }: {nixpkgs.overlays = default-overlays;})
          other-modules
          ./system-configs/${hostname}/configuration.nix
          home-manager.nixosModules.home-manager
          {
            home-manager = {
              useGlobalPkgs = true;
              useUserPackages = true;
              users.n8henrie = import ./home.nix;
            };
          }
        ];
      })
      (match_system ".*-linux$");

nixosConfigurations = builtins.mapAttrs (_: module:
      lib.nixosSystem {
        modules = [module];
      })
    self.nixosModules;

darwinConfigurations =
      builtins.mapAttrs
      (hostname: sys:
        with sys;
          darwin.lib.darwinSystem {
...
})
      (match_system ".*-darwin$");

nixopsConfigurations.default = {
      nixpkgs = nixpkgs-stable;
      network = {
        storage.legacy = {};
      };
      myRemoteMachine = {
        imports = [self.nixosModules.myRemoteMachine];
        deployment = {
          targetHost = "myRemoteMachine.domain.com";
          targetPort = 22;
        };
      };
    };
}

It doesn’t seem like I ended up needing anything from nixops show-physical.

Will also repost this to the relevant issue for to save other searchers a click. Thanks again!

1 Like

if you want that simplicity… and something just creates deployment shell scripts… you may like…

it under furious development.

Currently reworking it to use impure derivations from Add basic impure derivations · edolstra/nix@690e06b · GitHub

1 Like