ITSkillsCenter
Business Digital

Flakes Nix : essentiels et premier projet reproductible (tutoriel)

11 دقائق للقراءة

📍 Guide principal : NixOS pour développeurs et serveurs : reproductibilité totale en 2026
Cet article fait partie d’une série sur NixOS. Le guide principal pose les concepts de store, dérivations et générations qu’on suppose acquis ici.

Les flakes en deux phrases avant de plonger dans le code

Un flake est un dépôt git qui expose une API standardisée à Nix : il déclare ses entrées (dépendances vers d’autres flakes : nixpkgs, home-manager, agenix), il produit des sorties typées (devShells, packages, nixosConfigurations), et il fige la version exacte de chaque entrée dans un fichier flake.lock. Concrètement, un flake remplace les anciens shell.nix, default.nix et channels par un format unique, versionnable, qui rend chaque build identique d’une machine à l’autre.

Le manuel officiel marque toujours les flakes comme « experimental » mais l’écosystème entier en dépend : home-manager, agenix, colmena, deploy-rs, nixos-rebuild --flake. Apprendre à écrire un flake en 2026 n’est plus une option pour un développeur qui veut produire des environnements reproductibles. Ce tutoriel construit pas à pas un flake minimaliste pour un projet Node + PostgreSQL, le plus court chemin pour comprendre le format.

Prérequis

  • Nix installé (NixOS, ou Nix sur Linux/macOS via le Determinate Systems installer).
  • Flakes activés. Sur NixOS : nix.settings.experimental-features = [ "nix-command" "flakes" ];. Hors NixOS : ajouter experimental-features = nix-command flakes dans ~/.config/nix/nix.conf.
  • Un dépôt git initialisé localement — Nix exige que les fichiers d’un flake soient suivis par git pour être pris en compte.
  • Connaissance du concept de dérivation et de store ; sinon lire d’abord le guide principal.

Étape 1 — Initialiser un flake vide

La commande nix flake init génère un squelette minimal dans le dossier courant. Avant de la lancer, créez un dossier de projet et initialisez git ; sans git, Nix vous le rappellera avec une erreur explicite.

mkdir mon-app && cd mon-app
git init
nix flake init
git add flake.nix

Le fichier flake.nix généré contient le squelette de base : une description, une entrée nixpkgs pointant vers la branche stable, et un bloc outputs vide. C’est le minimum syntaxiquement valide ; à partir de là, on personnalise.

Étape 2 — Comprendre la structure d’un flake.nix

Voici un flake.nix commenté qui produit un devShell Node + PostgreSQL. Lisez-le en entier avant de copier — chaque bloc a un rôle précis.

{
  description = "Environnement de dev Node + PostgreSQL";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs { inherit system; };
      in {
        devShells.default = pkgs.mkShell {
          packages = with pkgs; [
            nodejs_22
            postgresql_17
            pnpm
            git
          ];

          shellHook = ''
            echo "Node $(node --version), Postgres $(psql --version)"
          '';
        };
      });
}

Le bloc inputs liste les dépendances : nixpkgs en version stable 25.11, et flake-utils qui fournit un helper pour générer les sorties pour x86_64-linux, aarch64-linux, x86_64-darwin et aarch64-darwin sans les répéter. Le bloc outputs est une fonction qui reçoit les inputs après résolution et renvoie un attrset de sorties. Ici on ne déclare qu’un seul devShell par défaut, mais on aurait pu déclarer aussi packages, apps, checks, nixosConfigurations.

Étape 3 — Le premier nix develop

Sauvegardez flake.nix, ajoutez-le à git (Nix ignore les fichiers non suivis), puis entrez dans le shell :

git add flake.nix
nix develop

La première fois, Nix télécharge nixpkgs (plusieurs centaines de mégaoctets compressés mais pas extraits), évalue la dérivation, télécharge les binaires depuis cache.nixos.org et bascule dans un nouveau shell. Le shellHook s’exécute et affiche les versions de Node et Postgres. Tapez which node : le chemin pointe sous /nix/store/...-nodejs-22.x.x/bin/node, pas vers /usr/bin/node. Vous êtes dans un environnement isolé.

Sortez avec exit ou Ctrl-D ; le shell parent retrouve son environnement d’origine. Le projet ne pollue rien à l’extérieur de son dossier.

Étape 4 — Inspecter le flake avec nix flake show et check

Deux commandes utiles dès le début. nix flake show affiche l’arbre des sorties exposées par le flake — une boussole pour comprendre ce qu’un flake offre quand on le découvre.

nix flake show

Sortie attendue : un arbre listant devShells.x86_64-linux.default avec le type « development environment ». Si vous ajoutez plus tard des packages ou des nixosConfigurations, ils apparaîtront ici. nix flake check évalue toutes les sorties pour s’assurer qu’aucune ne provoque d’erreur ; à utiliser avant chaque commit pour valider le flake comme on lance un linter.

nix flake check

Une sortie vide signifie que tout passe. Une erreur pointe la sortie qui échoue, avec une trace ; c’est aussi ce que la CI Nix exécute pour valider une PR.

Étape 5 — flake.lock : votre garantie de reproductibilité

À la première exécution de nix develop ou nix flake show, Nix a écrit flake.lock à côté de flake.nix. Ce fichier JSON contient le hash exact de chaque commit pris pour chaque input. C’est l’équivalent de package-lock.json ou Cargo.lock : il fige tout pour qu’un build dans six mois donne le même résultat qu’aujourd’hui.

cat flake.lock | head -30

La sortie montre une entrée par input avec son narHash, son rev (commit git), son type et son url. Commit toujours flake.lock dans git — c’est le contrat de reproductibilité avec vos coéquipiers et avec la version de demain de vous-même.

Étape 6 — Mettre à jour les inputs avec nix flake update

Vos dépendances vieillissent. Pour récupérer les dernières versions de nixpkgs et de flake-utils :

nix flake update

La commande met à jour tous les inputs à la fois. Si vous voulez ne mettre à jour qu’un input précis (la syntaxe a évolué dans les Nix récents : un argument positionnel remplace l’ancien --update-input) :

nix flake update nixpkgs

Après mise à jour, git diff flake.lock montre les changements de hash. Lancez nix flake check et nix develop pour valider qu’aucun paquet n’a été supprimé ou renommé. Si tout passe, commit le nouveau flake.lock.

Étape 7 — Ajouter direnv pour automatiser l’entrée du shell

Taper nix develop à chaque entrée du dossier devient vite fastidieux. direnv couplé à nix-direnv charge automatiquement le devShell quand vous entrez dans le dossier. Installez-les via votre flake utilisateur ou en système :

nix-env -iA nixpkgs.direnv nixpkgs.nix-direnv
# ou plus proprement, dans home-manager :
# programs.direnv.enable = true;
# programs.direnv.nix-direnv.enable = true;

Ajoutez à ~/.bashrc ou ~/.zshrc : eval "$(direnv hook zsh)" (ou bash). Dans le dossier du projet, créez un .envrc avec une ligne :

echo 'use flake' > .envrc
direnv allow

Au prochain cd vers le dossier, direnv charge le devShell ; au cd sortant, il restaure le shell parent. Plus jamais besoin de nix develop manuellement.

Étape 8 — Ajouter un package au flake

Un flake peut aussi exposer un binaire à construire. Ajoutez une sortie packages :

packages.default = pkgs.writeShellApplication {
  name = "hello-stack";
  text = ''
    echo "Hello depuis Node $(node --version)"
  '';
  runtimeInputs = [ pkgs.nodejs_22 ];
};

Reconstruisez avec nix build à la racine du dépôt :

nix build
./result/bin/hello-stack

nix build compile le package, dépose un lien symbolique result/ dans le dossier, et le binaire est exécutable. nix run ferait la même chose en une étape sans laisser de lien.

Étape 9 — Vérifier la reproductibilité sur une autre machine

C’est le test ultime du flake : pousser le dépôt git sur GitHub, le cloner ailleurs, et vérifier qu’on obtient le même environnement. Sur la machine de test :

git clone <url> mon-app && cd mon-app
nix develop
node --version
psql --version

Si flake.lock est bien commit, les deux versions affichées sont identiques bit-pour-bit à celles de la machine d’origine.

Erreurs fréquentes et leur résolution

Symptôme Cause Solution
error: file 'flake.nix' was not found in registry or filesystem Le fichier n’est pas tracké par git. git add flake.nix flake.lock
error: experimental Nix feature 'nix-command' is disabled Flakes pas activés au niveau utilisateur. Ajouter experimental-features = nix-command flakes dans ~/.config/nix/nix.conf ou dans la config NixOS.
error: attribute 'devShells.x86_64-linux.default' missing Sortie pas déclarée pour ce système. Vérifier que flake-utils.lib.eachDefaultSystem est bien utilisé.
direnv ne charge pas le shell direnv allow oublié, ou hook shell pas installé. Vérifier direnv hook zsh dans .zshrc, relancer direnv allow.
Build trop lent : tout recompile Cache binaire pas atteignable. Vérifier substituters dans nix.conf ; https://cache.nixos.org doit y figurer.

Le registre de flakes et les références courtes

La forme longue d’une URL de flake est github:NixOS/nixpkgs/nixos-25.11. Nix maintient aussi un registry qui mappe des noms courts vers ces URLs longues. Lancez nix registry list pour le voir. C’est ce qui permet d’écrire nix run nixpkgs#cowsay -- "hello" au lieu de la forme complète. Vous pouvez ajouter vos propres entrées avec nix registry add monorepo github:votre-org/nix-monorepo. Pour un projet partagé, le registre n’est pas ce qu’il faut éditer : préférez les inputs dans flake.nix qui sont versionnés.

Pourquoi les flakes battent les anciens channels

Avant les flakes, l’écosystème reposait sur les channels. Trois problèmes faisaient mal au quotidien. Premièrement, les channels sont par utilisateur, pas par projet. Si Alice est sur nixos-24.05 et Bob sur unstable, le même shell.nix donne deux résultats différents. Deuxièmement, les channels n’ont pas de lockfile : nix-channel --update ramène la dernière version, et il n’existe pas de moyen propre de figer une version. Troisièmement, le format shell.nix est libre — chacun structure son projet à sa façon.

Les flakes résolvent les trois en un coup : les inputs sont par-projet, le flake.lock fige tout, et la structure des sorties est standardisée.

Anatomie détaillée d’un flake.lock

Le contenu du flake.lock est un JSON avec une racine nodes qui décrit chaque dépendance résolue, un root qui pointe vers votre flake, et une version du format. Le bloc original conserve la spécification que vous avez écrite (nixos-25.11, qui est une branche mouvante). Le bloc locked fige le commit exact à un instant donné. Quand un coéquipier clone le repo, Nix lit locked et utilise ce commit précis, ignorant volontairement la mise à jour de la branche.

Trois patterns de workflow qui valent le coup

Le flake-template pour démarrer plus vite

Plutôt que de réécrire un flake.nix de zéro à chaque projet, exposez vos squelettes via un repo de templates : nix flake init -t github:votre-org/flake-templates#node-postgres.

Le devShell multi-langages

Plutôt qu’un devShell géant, déclarez plusieurs devShells nommés et activez le bon avec nix develop .#backend ou nix develop .#frontend.

devShells = {
  default = pkgs.mkShell { packages = [ pkgs.git ]; };
  backend = pkgs.mkShell { packages = with pkgs; [ go gopls ]; };
  frontend = pkgs.mkShell { packages = with pkgs; [ nodejs_22 pnpm ]; };
};

Les checks comme tests pré-merge

Une sortie checks.<system>.<nom> est exécutée par nix flake check et donne un signal binaire OK/échec. C’est le bon endroit pour faire passer un linter, un test unitaire qui ne dépend que des sources, ou un nixpkgs-fmt.

Les références d’inputs : github, gitlab, path, tarball

github: est le plus courant. gitlab: et sourcehut: existent par symétrie. git+https: ou git+ssh: ciblent un dépôt git arbitraire. path: pointe vers un dossier local, indispensable pendant le développement d’un input qu’on veut tester sans pousser ses commits :

inputs.monlib.url = "path:/home/alice/projets/monlib";

tarball: et file: permettent d’utiliser une archive distante ou locale comme input.

Versionner et publier un flake en bibliothèque

Un flake peut aussi exposer une bibliothèque réutilisable, un module NixOS, ou une fonction utilitaire. Le pattern : déclarez lib ou nixosModules en sortie, poussez le flake sur GitHub. Le tagging git devient important : pousser un tag sémantique v1.2.0 permet à vos consommateurs d’écrire github:votre-org/nix-utils/v1.2.0 et de figer une version stable.

Suite logique de la série

Ce tutoriel pose le format flake générique. La suite naturelle est Dev-shells par projet avec nix develop. Pour gérer ses propres dotfiles dans le même style déclaratif, Home-Manager : gérer son environnement utilisateur est la suite. Le guide principal NixOS situe les flakes dans l’écosystème complet.

Références officielles

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité