Day 60 — Colmena
Day 60 — Colmena
Stage VI · ~4h
Goal: Express a small hive with Colmena, deploy at least two nodes (one may be local), use tags, and know when Colmena wins over deploy-rs for fleet shape. Hosts pin NixOS 26.05.
deploy-rs is excellent node/profile oriented glue. Colmena shines when you think in hives: many hosts, tags (@web, @db), parallel apply, and a flake hive attribute. Real orgs often evaluate both; leave Day 70 with a deliberate choice, not two half-wired tools forever.
Why this day exists
One remote deploy proves the pipe. Fleets need selection, defaults, and blast-radius control. Colmena’s mental model maps cleanly onto roles you will build on Day 61.
Theory 1 — Hive mental model
meta (nixpkgs, specialArgs)
nodes
host-a → NixOS module stack
host-b → …
defaults → shared modules
| Concept | Role |
|---|---|
meta.nixpkgs |
Package set for evaluation |
defaults |
Applied to every node |
nodes.<name> |
Per-host config + deployment keys |
| tags | Selection subsets for apply |
specialArgs |
Inject libs/secrets helpers carefully |
Theory 2 — Flake-based Colmena (modern)
{
description = "Day 60 colmena hive";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
outputs = { self, nixpkgs, ... }: {
colmena = {
meta = {
nixpkgs = import nixpkgs { system = "x86_64-linux"; };
# specialArgs = { inherit something; };
};
defaults = { pkgs, ... }: {
time.timeZone = "UTC";
services.openssh.enable = true;
environment.systemPackages = [ pkgs.vim pkgs.curl ];
system.stateVersion = "26.05";
};
edge = { name, nodes, pkgs, ... }: {
deployment = {
targetHost = "edge.lab.internal";
targetUser = "root";
tags = [ "edge" "web" ];
};
networking.hostName = "edge";
imports = [ ./hosts/edge ];
};
core = {
deployment = {
targetHost = "core.lab.internal";
tags = [ "core" ];
# allowLocalDeployment = true; # when node is the machine itself
};
networking.hostName = "core";
imports = [ ./hosts/core ];
};
};
};
}nix shell nixpkgs#colmena -c colmena apply
nix shell nixpkgs#colmena -c colmena apply --on @web
nix shell nixpkgs#colmena -c colmena apply-local --sudo # local node patternsCLI details (colmena apply --on host)—run colmena --help for your version. Some pins expose colmenaHive—follow current Colmena flake docs if colmena attr alone is insufficient.
Theory 4 — deploy-rs vs Colmena (honest)
| Dimension | deploy-rs | Colmena |
|---|---|---|
| Flake-native profiles | Strong | Strong hive attr |
| Multi-host UX | Nodes map | Hive + tags first-class |
| Magic rollback | Notable feature | Different tooling |
| Learning curve | Lower for 1 node | Pays off at N>2 |
| Ecosystem | Serokell | zhaofengli / community |
House rule suggestion: deploy-rs for 1–2, Colmena when tags appear—or standardize on one to reduce cognitive load. Pick deliberately by Day 70 and document in FLEET-TOOLING.md.
Theory 5 — Build & push options
Colmena can build locally or on targets depending on deployment.buildOnTarget and related options:
deployment = {
targetHost = "edge.lab.internal";
buildOnTarget = false;
# tags = [ "edge" ];
};| Mode | When |
|---|---|
| Build local, push | Fast admin machine; matching arch |
| Build on target | Weak laptop; strong nodes |
| Remote builder + cache | Day 54 + 53 machinery |
Combine with Day 53 caches so N nodes do not compile N times.
Theory 6 — Evaluation cost and hive growth
Large hives evaluate all nodes for some operations. Habits:
| Habit | Why |
|---|---|
| Thin defaults | Shared eval cost is real |
| Avoid importing desktop on servers | Closure + eval bloat |
colmena eval / dry-run |
Catch errors before blast |
| Split lab vs prod flakes if needed | Cognitive firewall |
Worked example — two-node visible drift control
# hosts/edge/default.nix
{ pkgs, ... }:
{
environment.etc."role".text = "edge\n";
networking.firewall.allowedTCPPorts = [ 80 ];
# services.caddy… optional
}# hosts/core/default.nix
{
environment.etc."role".text = "core\n";
}colmena apply --on edge
ssh edge cat /etc/roleLab — multi-step
Suggested notes: ~/lab/90daysofx/02-nixos/day60
Step 1 — Second node
Provision second VM or treat laptop as allowLocalDeployment node + one remote.
Step 2 — Express hive
Move shared SSH/firewall defaults into defaults; keep host-only bits in nodes. Commit lockfile.
Step 3 — First full apply
colmena applyCapture output; note parallel vs serial behavior.
Step 4 — Tag apply
Tag one host canary; apply only canary; then the rest.
Step 5 — Parallel observation
Apply both; note wall clock vs serial deploy-rs mental model.
Step 6 — Intentional drift fix
SSH to a node; make a manual /etc change on a managed file; re-apply Colmena; confirm declarative truth wins (for managed paths).
Step 7 — Tool choice note
FLEET-TOOLING.md: when to use Colmena vs deploy-rs in your monorepo; one default for Capstone A.
Step 8 — Optional eval-only
colmena eval -E '{ nodes, ... }: builtins.attrNames nodes'(Adjust to supported eval interface in your Colmena version.)
Step 9 — Failure notes
Document one failed apply (intentional or accidental) and recovery. Link Day 89 rollback thinking.
Parallelism and blast radius
colmena apply --on @web
│
├─ edge-1 ─┐
├─ edge-2 ─┼─ simultaneous pressure on cache/builder
└─ edge-3 ─┘
| Control | Use |
|---|---|
| Tags / canary | Limit who changes first |
| Cache warm | Avoid N compile storms |
| Console access | Survive firewall mistakes |
| One tool default | deploy-rs or Colmena per host class |
Never demo “apply all prod” on day one of Colmena.
Common gotchas
| Symptom / mistake | What to do |
|---|---|
| Hive not discovered | Flake output name colmena / colmenaHive per version docs |
| Wrong nixpkgs system | meta.nixpkgs system must match builds |
| Tags typo | List tags; dry-run selection |
| Local node confusion | allowLocalDeployment / apply-local |
| Double firewall lockout | Console access |
| Gigantic simultaneous compiles | Cache + buildOnTarget policy |
stateVersion missing on defaults |
Set "26.05" deliberately |
| Mixing deploy-rs + Colmena on same host ad hoc | One tool per host class |
Checkpoint
- Hive evaluates
- Two nodes deployed (or local+remote)
- Tag-filtered apply works
- Drift overwritten by apply
- Tooling choice written
Commit
git add .
git commit -m "day60: colmena hive and tagged apply"Write three personal gotchas before continuing.
Tomorrow
Day 61 — Shared modules / roles: stop copy-pasting host configs.