My NixOS kubernetes manager nk3 has been excellent over the last year. Effortless config changes and upgrades with no downtime. So excellent however, that I’ve found myself wanting a similar workflow for managing everything else that might be running NixOS - for example, my routers.
Introducing nixa; a general-purpose config management tool with the aim of providing an ansible-like workflow for NixOS servers - without the downsides of ansible.
The basic goal of nixa is to generate and apply configuration.nix
to many hosts as defined by an inventory.yaml
, and to perform nixos-native OS upgrades.
Like in ansible, inventory.yaml
defines groups, host membership, and templates to apply to the groups:
---
routers:
hosts:
- 10.38.154.98
- 10.38.154.87
- 10.38.154.160
templates:
- lxc.nix
nix-channel: 24.11
We will not experience the primary drawback of ansible (side effects) - since configuration.nix
is generated and applied on each execution, if we remove a package or service from our templates, it Will be actually removed from the target hosts. (If you remove, say a firewall rule from an ansible role - ansible will not actually remove that rule, it simply forgets about it).
As of today, nixa
only knows how to apply a single config template:
nixa > just
black .
All done! ✨ 🍰 ✨
4 files left unchanged.
flake8 . --ignore=E501,W503
nix-shell . --run 'python3 main.py'
d14d90c2-4e69-5cb0-ac9d-e1ca0ce71c19 is reachable
0a6dd2c3-bc17-59c5-859b-b9c9921b7961 is reachable
86da48cb-c4ad-548b-9859-dfd7a3ee1d87 is reachable
applying template lxc.nix to routers: ['d14d90c2-4e69-5cb0-ac9d-e1ca0ce71c19', '0a6dd2c3-bc17-59c5-859b-b9c9921b7961', '86da48cb-c4ad-548b-9859-dfd7a3ee1d87']
d14d90c2-4e69-5cb0-ac9d-e1ca0ce71c19 modified:
---
+++
@@ -8,7 +8,6 @@
networking.hostName = "d14d90c2-4e69-5cb0-ac9d-e1ca0ce71c19";
environment.systemPackages = with pkgs; [
- htop
busybox
];
Rebuilding NixOS on d14d90c2-4e69-5cb0-ac9d-e1ca0ce71c19
Rebooting d14d90c2-4e69-5cb0-ac9d-e1ca0ce71c19
d14d90c2-4e69-5cb0-ac9d-e1ca0ce71c19 is reachable
0a6dd2c3-bc17-59c5-859b-b9c9921b7961 modified:
---
+++
@@ -8,7 +8,6 @@
networking.hostName = "0a6dd2c3-bc17-59c5-859b-b9c9921b7961";
environment.systemPackages = with pkgs; [
- htop
busybox
];
Rebuilding NixOS on 0a6dd2c3-bc17-59c5-859b-b9c9921b7961
Rebooting 0a6dd2c3-bc17-59c5-859b-b9c9921b7961
0a6dd2c3-bc17-59c5-859b-b9c9921b7961 is reachable
86da48cb-c4ad-548b-9859-dfd7a3ee1d87 modified:
---
+++
@@ -8,7 +8,6 @@
networking.hostName = "86da48cb-c4ad-548b-9859-dfd7a3ee1d87";
environment.systemPackages = with pkgs; [
- htop
busybox
];
Rebuilding NixOS on 86da48cb-c4ad-548b-9859-dfd7a3ee1d87
Rebooting 86da48cb-c4ad-548b-9859-dfd7a3ee1d87
86da48cb-c4ad-548b-9859-dfd7a3ee1d87 is reachable
Rolling OS upgrades are done using --upgrade
. This effectively just does nixos-rebuild boot --upgrade
and reboots:
nixa > just upgrade
black .
All done! ✨ 🍰 ✨
4 files left unchanged.
flake8 . --ignore=E501,W503
nix-shell . --run 'python3 main.py -u'
81df66bb-c238-59ba-923b-1d3360ff4c41 is reachable
b55416a9-f3fc-59d5-b5f8-d40f13e815a1 is reachable
ab984010-325a-57fa-a04c-78fea2d5fbad is reachable
routers:
--> upgrading 81df66bb-c238-59ba-923b-1d3360ff4c41:
enforcing nixos channel nixos-24.11
20 paths fetched
21 derivations built
test:
--> upgrading b55416a9-f3fc-59d5-b5f8-d40f13e815a1:
enforcing nixos channel nixos-24.11
4 paths fetched
21 derivations built
--> upgrading ab984010-325a-57fa-a04c-78fea2d5fbad:
enforcing nixos channel nixos-24.11
4 paths fetched
21 derivations built
Whatever channel nix-channel:
is set to in the inventory is enforced. For regular ‘daily’ upgrades, just run --upgrade
. For Release upgrades, go increment the channel in the inventory, then run --upgrade
.
nixa
is a word sandwich of ’nixos apply’ or something like that.