No description
Find a file
Zhaofeng Li 0e0a1e84f0 Make flake resolution (slightly) less terrible
Instead of using `path:` which always copies the entire directory,
we now try to resolve the Flake URI using `nix flake metadata` which
may give us a `git+file:`.
2021-10-25 23:38:10 -07:00
.github/workflows .github: Fix job :/ 2021-09-19 22:05:51 -07:00
src Make flake resolution (slightly) less terrible 2021-10-25 23:38:10 -07:00
.envrc .envrc: Use nix-direnv with use flake 2021-08-26 13:15:04 -07:00
.gitattributes Support per-node Nixpkgs overrides and local deployment 2020-12-19 15:10:22 -08:00
.gitignore .envrc: Use nix-direnv with use flake 2021-08-26 13:15:04 -07:00
Cargo.lock Allow selecting ssh user dynamically 2021-10-23 15:06:56 +02:00
Cargo.toml Allow selecting ssh user dynamically 2021-10-23 15:06:56 +02:00
default.nix default.nix: Use cargoLock.lockFile 2021-08-26 13:21:18 -07:00
flake-compat.nix Make dev environment flake-compatible 2021-06-29 01:02:43 -07:00
flake.lock flake.lock: Update nixpkgs 2021-10-25 21:47:43 -07:00
flake.nix flake.nix: Clean up and add overlay 2021-09-19 22:03:39 -07:00
LICENSE LICENSE: Add contributors 2021-08-01 14:20:14 -07:00
README.md Add deployment.keys.<name>.uploadAt 2021-08-24 23:25:46 -07:00
shell.nix Make dev environment flake-compatible 2021-06-29 01:02:43 -07:00

Colmena

Build

Colmena is a simple, stateless NixOS deployment tool modeled after NixOps and Morph, written in Rust. It's a thin wrapper over Nix commands like nix-instantiate and nix-copy-closure, and supports parallel deployment.

Now with 100% more flakes! See Tutorial with Flakes below.

$ colmena apply --on @tag-a
[INFO ] Enumerating nodes...
[INFO ] Selected 7 out of 45 hosts.
  (...) ✅ 0s Build successful
  sigma 🕗 7s copying path '/nix/store/h6qpk8rwm3dh3zsl1wlj1jharzf8aw9f-unit-haigha-agent.service' to 'ssh://root@sigma.redacted'...
  theta ✅ 7s Activation successful
  gamma 🕘 8s Starting...
  alpha ✅ 1s Activation successful
epsilon 🕗 7s copying path '/nix/store/fhh4rfixny8b21l6jqzk7nqwxva5k20h-nixos-system-epsilon-20.09pre-git' to 'ssh://root@epsilon.redacted'...
   beta 🕗 7s removing obsolete file /boot/kernels/z28ayg10kpnlrz0s2qrb9pzv82lc20s2-initrd-linux-5.4.89-initrd
  kappa ✅ 2s Activation successful

Colmena is still an early prototype.

Installation

Colmena doesn't have a stable release yet. To install the latest development version to the user profile, use default.nix:

nix-env -if default.nix

Tutorial

See Tutorial with Flakes for usage with Nix Flakes.

Colmena should work with your existing NixOps and Morph configurations with minimal modification. Here is a sample hive.nix with two nodes, with some common configurations applied to both nodes:

{
  meta = {
    # Override to pin the Nixpkgs version (recommended). This option
    # accepts one of the following:
    # - A path to a Nixpkgs checkout
    # - The Nixpkgs lambda (e.g., import <nixpkgs>)
    # - An initialized Nixpkgs attribute set
    nixpkgs = <nixpkgs>;

    # You can also override Nixpkgs by node!
    nodeNixpkgs = {
      node-b = ./another-nixos-checkout;
    };

    # If your Colmena host has nix configured to allow for remote builds
    # (for nix-daemon, your user being included in trusted-users)
    # you can set a machines file that will be passed to the underlying
    # nix-store command during derivation realization as a builders option.
    # For example, if you support multiple orginizations each with their own
    # build machine(s) you can ensure that builds only take place on your
    # local machine and/or the machines specified in this file.
    # machinesFile = ./machines.client-a;
  };

  defaults = { pkgs, ... }: {
    # This module will be imported by all hosts
    environment.systemPackages = with pkgs; [
      vim wget curl
    ];

    # By default, Colmena will replace unknown remote profile
    # (unknown means the profile isn't in the nix store on the
    # host running Colmena) during apply (with the default goal,
    # boot, and switch).
    # If you share a hive with others, or use multiple machines,
    # and are not careful to always commit/push/pull changes
    # you can accidentaly overwrite a remote profile so in those
    # scenarios you might want to change this default to false. 
    # deployment.replaceUnknownProfiles = true;
  };

  host-a = { name, nodes, ... }: {
    # The name and nodes parameters are supported in Colmena,
    # allowing you to reference configurations in other nodes.
    networking.hostName = name;
    time.timeZone = nodes.host-b.config.time.timeZone;

    boot.loader.grub.device = "/dev/sda";
    fileSystems."/" = {
      device = "/dev/sda1";
      fsType = "ext4";
    };
  };

  host-b = {
    # Like NixOps and Morph, Colmena will attempt to connect to
    # the remote host using the attribute name by default. You
    # can override it like:
    deployment.targetHost = "host-b.mydomain.tld";

    # It's also possible to override the target SSH port.
    # For further customization, use the SSH_CONFIG_FILE
    # environment variable to specify a ssh_config file.
    deployment.targetPort = 1234;

    # Override the default for this target host
    deployment.replaceUnknownProfiles = false;

    # You can filter hosts by tags with --on @tag-a,@tag-b.
    # In this example, you can deploy to hosts with the "web" tag using:
    #    colmena apply --on @web
    # You can use globs in tag matching as well:
    #    colmena apply --on '@infra-*'
    deployment.tags = [ "web" "infra-lax" ];

    time.timeZone = "America/Los_Angeles";

    boot.loader.grub.device = "/dev/sda";
    fileSystems."/" = {
      device = "/dev/sda1";
      fsType = "ext4";
    };
  };
}

The full set of options can be found at src/nix/eval.nix. Run colmena build in the same directory to build the configuration, or do colmena apply to build and deploy it to all nodes.

Tutorial with Flakes

To use with Nix Flakes, create outputs.colmena in your flake.nix.

Here is a short example:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };
  outputs = { nixpkgs, ... }: {
    colmena = {
      meta = {
        inherit nixpkgs;
      };

      # Also see the non-Flakes hive.nix example above.
      host-a = { name, nodes, pkgs, ... }: {
        boot.isContainer = true;
        time.timeZone = nodes.host-b.config.time.timeZone;
      };
      host-b = {
        deployment = {
          targetHost = "somehost.tld";
          targetPort = 1234;
          targetUser = "luser";
        };
        boot.isContainer = true;
        time.timeZone = "America/Los_Angeles";
      };
    };
  };
}

The full set of options can be found at src/nix/eval.nix. Run colmena build in the same directory to build the configuration, or do colmena apply to build and deploy it to all nodes.

colmena introspect

Sometimes you may want to extract values from your Hive configuration for consumption in another program (e.g., OctoDNS). To do that, create a .nix file with a lambda:

{ nodes, pkgs, lib, ... }:
# Feels like a NixOS module - But you can return any JSON-serializable value
lib.attrsets.mapAttrs (k: v: v.config.deployment.targetHost) nodes

Then you can evaluate with:

colmena introspect your-lambda.nix

colmena apply-local

For some machines, you may still want to stick with the manual nixos-rebuild-type of workflow. Colmena allows you to build and activate configurations on the host running Colmena itself, provided that:

  1. The node must be running NixOS.
  2. The node must have deployment.allowLocalDeployment set to true.
  3. The node's attribute name must match the hostname of the machine.

If you invoke apply-local with --sudo, Colmena will attempt to elevate privileges with sudo if it's not run as root. You may also find it helpful to set deployment.targetHost to null if you don't intend to deploy to the host via SSH.

As an example, the following hive.nix includes a node (laptop) that is meant to be only deployed with apply-local:

{
  meta = {
    nixpkgs = ./deps/nixpkgs-stable;

    # I'd like to use the unstable version of Nixpkgs on
    # my desktop machines.
    nodeNixpkgs = {
      laptop = ./deps/nixpkgs-unstable;
    };
  };

  # This attribute name must match the output of `hostname` on your machine
  laptop = { name, nodes, ... }: {
    networking.hostName = "laptop";

    deployment = {
      # Allow local deployment with `colmena apply-local`
      allowLocalDeployment = true;

      # Disable SSH deployment. This node will be skipped in a
      # normal`colmena apply`.
      targetHost = null;
    };

    # Rest of configuration...
  };

  server-a = { pkgs, ... }: {
    # This node will use the default Nixpkgs checkout specified
    # in `meta.nixpkgs`.

    # Rest of configuration...
  };
}

On laptop, run colmena apply-local --sudo to activate the configuration.

Secrets

Colmena allows you to upload secret files to nodes that will not be stored in the Nix store. It implements a subset of the deployment.keys options supported by NixOps.

For example, to deploy DNS-01 credentials for use with security.acme:

{
  shared-box = {
    security.acme.certs."my-site.tld".credentialsFile = "/run/keys/acme-credentials.secret";
    deployment.keys."acme-credentials.secret" = {
      # Alternatively, `text` (string) or `keyFile` (path to file)
      # may be specified.
      keyCommand = [ "vault" "read" "-field=env" "secret/dns01" ];

      destDir = "/run/keys";       # Default: /run/keys
      user = "acme";               # Default: root
      group = "nginx";             # Default: root
      permissions = "0640";        # Default: 0600

      uploadAt = "pre-activation"; # Default: pre-activation, Alternative: post-activation
    };
    # Rest of configuration...
  };
}

Take note that if you use the default path (/run/keys), the secret files are only stored in-memory and will not survive reboots. To upload your secrets without performing a full deployment, use colmena upload-keys.

Parallelism

Colmena is built from the ground up to support parallel deployments. Evaluation, build, and deployment of node configurations can happen at the same time. This parallelism can be controlled primarily through two flags:

  • --limit <number>: Number of hosts to deploy at once in the final step (pushing closures and activating new profiles).
  • --eval-node-limit <number>: By default, Colmena will automatically determine the maximum number of nodes to evaluate at the same time according to available RAM. This flag allows you to set the limit to a predetermined value.

Environment variables

  • SSH_CONFIG_FILE: Path to a ssh_config file

Current limitations

  • It's required to use SSH keys to log into the remote hosts, and interactive authentication will not work.
  • Error reporting is lacking.

Licensing

Colmena is available under the MIT License.