Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

1134 6 20

Clone this repository

https://tangled.org/adam.robins.wtf/sower https://tangled.org/did:plc:njukxjgz7nek2mvu5d7jis3y/sower
git@tangled.org:adam.robins.wtf/sower git@tangled.org:did:plc:njukxjgz7nek2mvu5d7jis3y/sower

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

Sower#

Sower is a deployment and lifecycle management tool for Nix based configurations, including NixOS and Home-Manager.

With sower we sow the seeds of our systems.

  • A seed is an extra bundle of metadata for an artifact path, e.g. a Nix store path.
  • Seed metadata includes a set of tags, with git and user-provided tags.
  • A garden defines seeds they want to subscribe to.
  • Seeds are submitted to a server to be used for deployments.

WARNING This project is experimental and is not recommended for production installation. One of the goals is never break deployments, but it is not guaranteed yet. I'm only using this in a homelab with approximately a dozen gardens. This means the risk to me of breaking deployments is moderately low.

I'd love for others to get value out of what I'm building here. Please reach out if you're a user, I want to chat. :)

Installation#

Read the NixOS modules for the server and the garden. There is an example in nix/tests/e2e.nix

  1. An example server config exists in nix/tests/e2e.nix
  2. An example garden config is below.

Good luck, everyone's counting on you.

Components#

  • Server including Phoenix LiveView web interface.
  • Always-on end-system daemon (Garden) with bi-directional communication to the Server over real-time WebSocket connection.
  • Activator used by the Garden for running limited, specific actions as root, over a systemd initiated socket.
  • CLI for submitting seeds including a full code to submitted builder.

Gardens#

Gardens are an always on client, which have full control over what the seeds from the server can or will do. Through the garden's configuration, you can determine what the garden should subscribe to and what should happen when working with seeds for deployment.

Subscriptions#

Subscriptions are the main controls for how systems are deployed. They include:

  • A set of seed tag matching rules
  • Schedule in cron format for pull-based deployments
  • A policy controlling which deployment actions are permitted, and when

Policy#

Each subscription carries a policy map of named rules. Each rule permits a set of actions (stage, activate, restart) for a set of triggers (manual, scheduled, realtime, poll_on_connect), optionally constrained to a time window. Multiple rules are OR-ed; the highest-disruption permitted action wins. See docs/spec-deployment-policy.org for the full specification.

Example garden config#

{
  age.secrets.sower-api-token = {
    file = cfg.access_token_secret;
    owner = "sower-garden";
  };

  services.sower = {
    activator = {
      package = inputs.sower-next.packages.${pkgs.stdenv.hostPlatform.system}.activator;
      allowedGroups = [ "users" ];
    };

    garden = {
      enable = true;
      accessTokenFile = config.age.secrets.sower-api-token.path;
      package = inputs.sower-next.packages.${pkgs.stdenv.hostPlatform.system}.garden;

      settings = {
        access_token_file = config.age.secrets.sower-api-token.path;
        endpoint = "http://localhost:7150";

        subscriptions = {
          ${config.networking.hostName} = {
            seed_name = config.networking.hostName;
            seed_type = "nixos";
            rules = [ "git_branch=main" ];
            # https://hexdocs.pm/crontab/cron_notation.html
            schedule = "0 3 * * *";
            timezone = "America/New_York";

            policy = {
              # Allow manual activations anytime.
              manual = {
                actions = [ "activate" ];
                triggers = [ "manual" ];
              };
              # Scheduled / poll-on-connect deploys may stage, activate, and
              # reboot — but only inside the maintenance window.
              maintenance = {
                actions = [ "stage" "activate" "restart" ];
                triggers = [ "scheduled" "poll_on_connect" ];
                window = {
                  days = [ "mon" "tue" "wed" "thu" "fri" "sat" "sun" ];
                  time_start = "02:00";
                  time_end = "04:00";
                };
              };
            };
          };
        };
      };
    };
  };

  users.users.adam.extraGroups = [ "sower-activator" ];
};

Disclaimer#

This project uses coding agents for assisting and producing code.