Aller au contenu
Logo Nix

Nix Flakes - reproductibilité et multi-machines

Avant les flakes, une config NixOS s’appuyait sur un canal (channel) pour résoudre nixpkgs. Le problème : deux machines avec le même configuration.nix peuvent avoir des résultats différents si leurs canaux pointent vers des commits différents. Pas d’erreur, pas d’avertissement - juste un comportement qui diverge silencieusement.

Les flakes règlent ça en rendant la source de chaque dépendance explicite et verrouillée dans un fichier git.

Un flake c’est un flake.nix avec deux champs : inputs et outputs.

{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs, ... }: {
# ce qu'on expose
};
}

inputs déclare les dépendances - d’autres flakes, des repos GitHub, des archives. outputs est une fonction qui reçoit ces inputs résolus et retourne ce qu’on veut exposer.

C’est là que le côté fonctionnel du langage Nix se voit le mieux. outputs est une fonction pure : mêmes inputs, mêmes outputs, sans effet de bord. Rien ne dépend de ce qui est installé sur la machine, rien n’est résolu depuis l’environnement local. Deux machines, même flake, même résultat.

Au premier build, Nix crée un flake.lock qui fixe le commit exact de chaque input :

{
"nodes": {
"nixpkgs": {
"locked": {
"rev": "a3ed7406349a9335cb4c2a71369b697cecd9d351",
"type": "github",
"owner": "NixOS",
"repo": "nixpkgs"
}
}
}
}

Ce fichier est à commiter. C’est lui qui garantit que deux machines qui font nixos-rebuild switch --flake . depuis le même commit git obtiennent exactement le même résultat.

Pour mettre à jour :

Fenêtre de terminal
nix flake update # tous les inputs
nix flake update nixpkgs # un input précis

L’usage principal pour une config perso. Chaque clé correspond à une machine :

outputs = { self, nixpkgs, ... }: {
nixosConfigurations = {
laptop = nixpkgs.lib.nixosSystem {
modules = [ ./hosts/laptop ];
};
serveur = nixpkgs.lib.nixosSystem {
modules = [ ./hosts/serveur ];
};
};
};
Fenêtre de terminal
sudo nixos-rebuild switch --flake .#laptop

C’est honnêtement ce qui m’avait vendu les flakes avant même d’avoir une config NixOS. Rentrer dans le projet d’un inconnu qui a un flake.nix :

Fenêtre de terminal
nix develop

Ca télécharge exactement les dépendances déclarées dans un shell isolé. En sortant, rien ne reste installé globalement. Plus besoin de lire un README pour savoir quoi installer - si le projet a un flake, l’environnement est là.

devShells.x86_64-linux.default = pkgs.mkShell {
packages = with pkgs; [ nodejs python3 postgresql ];
};

Un flake peut aussi exposer des packages buildables :

Fenêtre de terminal
nix build .#mon-package

Le résultat atterrit dans ./result, un lien symbolique vers le store. Je m’en sers par exemple pour builder une image Proxmox directement depuis le flake :

Fenêtre de terminal
nix build .#proxmox-image

Ma config gère quatre machines depuis un seul flake : laptop, WSL, VM de test, serveur. Le WSL a des besoins différents des autres - il faut le module NixOS-WSL pour que l’intégration avec Windows fonctionne correctement. J’ai donc deux helpers distincts :

outputs = { self, nixpkgs, home-manager, nixos-wsl, ... }@inputs:
let
mkHost = host: nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs; };
modules = [
./hosts/${host}
home-manager.nixosModules.home-manager
{ home-manager.users.tim = import ./home/tim; }
];
};
mkWsl = host: nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
nixos-wsl.nixosModules.wsl
./hosts/${host}
home-manager.nixosModules.home-manager
{ home-manager.users.tim = import ./home/tim; }
];
};
in {
nixosConfigurations = {
laptop = mkHost "laptop";
wsl = mkWsl "wsl";
};
};

Home Manager est branché directement dans le flake - pas géré séparément. La config utilisateur ./home/tim est la même sur toutes les machines : même shell, mêmes outils, peu importe où je me connecte. L’article suivant rentre dans le détail.

Fenêtre de terminal
# Voir ce qu'expose un flake
nix flake show
# Vérifier la structure sans builder
nix flake check
# Mettre à jour le lock file
nix flake update
# Builder un output
nix build .#nom-du-package
# Entrer dans un devShell
nix develop
# Appliquer une config NixOS
sudo nixos-rebuild switch --flake .#nom-machine