Deploy and infrastructure batteries
A fleet that builds NixOS hosts often also deploys them, writes DNS records, writes Kubernetes manifests and exports modules for other flakes. Batteries do each of these from a sink. This example uses all of them. The fleet spawns hosts, a cluster and an export. Each is a kind with its own sinks.
# Hosts and a cluster in one environment. Hosts build NixOS and join a colmena hive; deploy-rs reads the built configs.{ yen, lib, inputs, ...}:let b = yen.lib.batteries;in{ yen.kinds.fleet = { spawns = { host = _: yen.host; cluster = _: yen.cluster; export = _: yen.export; }; sinks = { out.reduce = yen.lib.reduce.merge; # each host gives its own NixOS modules to the hive colmena = { # (1) reduce = yen.lib.reduce.merge; out = b.deploy.colmena { from = yen.kinds.fleet.sinks.colmena; inherit (inputs.colmena.lib) makeHive; meta.nixpkgs = inputs.nixpkgs.legacyPackages.x86_64-linux; }; }; # dns records as terraform, built with terranix terraform.out = b.terranix.configuration { # (2) from = yen.kinds.fleet.sinks.terraform; inherit (inputs.terranix.lib) terranixConfiguration; system = "x86_64-linux"; name = _: "dns"; }; # machines write NixOS config into each other (oddlama's nodes.<name>) nodes.reduce = yen.lib.reduce.elements; # (3) }; }; yen.kinds.host = { fields.addr = yen.lib.str; sinks.nixos.out = b.nixos.system { # (4) from = yen.kinds.host.sinks.nixos; inherit (inputs) nixpkgs; }; }; yen.kinds.cluster.sinks.k8s.out = b.k8s.manifests { # (5) from = yen.kinds.cluster.sinks.k8s; inherit lib; }; # an export never builds a machine: what lands on it is nixosModules.<name> for other flakes yen.kinds.export.sinks.nixos.out = b.exports.modules { # (6) from = yen.kinds.export.sinks.nixos; output = "nixosModules"; };
yen.fleet.main = { }; yen.host = { lb.addr = "10.0.0.1"; web.addr = "10.0.0.2"; }; yen.cluster.axon = { }; yen.export.base = { }; # deploy-rs: one node per built NixOS config yen.flake = { out, ... }: out // b.deploy.deploy-rs { # (7) configurations = out.nixosConfigurations; hostname = n: _: yen.host.${n}.addr; activate = _: c: inputs.deploy-rs.lib.x86_64-linux.activate.nixos c; };}Each battery is the value of one key (out, reduce or a NixOS key). It gets the sink it reads as a ref in from.
(1)collects the modules of every host in a colmena hive. Theoutkey buildscolmenaHive.(2)collects terraform modules and buildsterraformConfigurations.dnswith terranix.(3)has noout. Machines give modules to each other through it, andelementskeeps who gave what.(4)buildsnixosConfigurationsfor each host.(5)merges the manifests of a cluster with the module system.(6)is an export. It builds no machine: it givesnixosModules.<name>for other flakes.(7)is a function inyen.flake. It gives one deploy-rs node for each built NixOS configuration.
The aspects fill these sinks. machine is active at every host and at the export base.
hive gives to the sinks colmena, terraform and nixos.
{ yen, ... }:let b = yen.lib.batteries;in{ yen.aspects.machine = { at = [ yen.kinds.host yen.export.base # the same base, exported for other flakes ]; nixos = { boot.loader.grub.device = "nodev"; fileSystems."/" = { device = "/dev/sda1"; fsType = "ext4"; }; nixpkgs.hostPlatform = "x86_64-linux"; system.stateVersion = "25.05"; }; }; yen.aspects.hive = { at = yen.kinds.host; colmena = # (1) { host, ... }: { ${host.name} = { deployment.targetHost = host.addr; imports = [ { networking.hostName = host.name; } ]; }; }; terraform = # (2) { host, ... }: { resource.cloudflare_record.${host.name} = { name = host.name; content = host.addr; type = "A"; }; }; nixos = b.distributed.nodes { from = yen.kinds.fleet.sinks.nodes; }; # (3) }; # lb writes into web: its backends know the load balancer yen.aspects.lb-writes-web = { at = yen.host.lb; nodes = # (4) { host, ... }: { web.networking.extraHosts = "${host.addr} lb.internal"; }; }; yen.aspects.ingress = { at = yen.cluster.axon; k8s.ingress.web = { # (5) host = "web.example.org"; port = 80; }; }; yen.aspects.ingress-tls = { at = yen.cluster.axon; k8s.ingress.web.tls = true; # (6) };}(1) and (2) give one host’s hive entry and DNS record, read from host.addr. At (3), distributed.nodes reads
what other machines wrote for this one. At (4), lb writes into web: its backends know the load balancer.
(5) and (6) are two aspects at one cluster. Both write the ingress of axon, and the module system merges them:
{ axon = { ingress = { web = { host = "web.example.org"; port = 80; tls = true; }; }; };}The batteries
Section titled “The batteries”Each battery with the value it is and what it gives:
| Battery | Value of | Gives |
|---|---|---|
deploy.colmena { from; makeHive; meta; } |
out of a merge sink of { <host> = module; } |
colmenaHive |
deploy.deploy-rs { configurations; hostname; activate; sshUser; } |
a function, in yen.flake |
deploy.nodes from built configs |
terranix.configuration { from; terranixConfiguration; system; name; } |
out of a terraform module sink |
terraformConfigurations.<name> |
k8s.manifests { from; lib; output; } |
out of a sink of manifest modules |
k8sManifests.<scope name>, merged with the module system |
distributed.nodes { from; } |
a NixOS key | the modules other machines wrote for this one |
exports.modules { from; output; } |
out of a module sink at an export scope |
<output>.<scope name> for other flakes |