Les flakes sont le mécanisme de reproductibilité native de Nix. Un fichier flake.nix déclare des dépendances versionnées (les inputs), et flake.lock verrouille leurs révisions exactes. Résultat : chaque membre de l'équipe, chaque serveur CI et chaque machine de production obtient exactement le même environnement. Ce guide couvre la structure complète d'un flake, les commandes de gestion, et la migration depuis shell.nix.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Créer un flake avec
nix flake initet comprendre sa structure - Déclarer des inputs (nixpkgs, flake-utils) et les verrouiller avec
flake.lock - Définir des outputs : devShells, packages, apps
- Construire, exécuter et inspecter un flake
- Convertir un projet
shell.nixclassique vers les flakes - Supporter plusieurs architectures avec
flake-utils
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Les channels Nix classiques, décrits dans Import, factorisation et pinning, posent un problème fondamental : chaque machine peut pointer vers une révision différente de nixpkgs. Même avec fetchTarball et un hash, le pinning reste manuel et fragile.
Les flakes résolvent ce problème en pratique quotidienne :
- Votre collègue clone votre dépôt et obtient les mêmes versions de Python, Node.js et tous les outils, automatiquement
- La CI construit avec les mêmes dépendances que votre poste, sans configuration supplémentaire
- Vous mettez à jour nixpkgs d'un seul
nix flake update, avec un diff vérifiable dansflake.lock - Vous composez plusieurs sources (nixpkgs stable, un overlay privé, un outil depuis GitHub) dans un seul fichier déclaratif
Activer les flakes dans votre configuration Nix
Section intitulée « Activer les flakes dans votre configuration Nix »Les flakes sont une fonctionnalité expérimentale qu'il faut activer explicitement. Si vous avez suivi le guide d'installation, c'est déjà fait.
Vérifiez votre configuration :
grep experimental-features ~/.config/nix/nix.conf 2>/dev/null || \grep experimental-features /etc/nix/nix.conf 2>/dev/nullexperimental-features = nix-command flakesSi la ligne est absente :
mkdir -p ~/.config/nixecho "experimental-features = nix-command flakes" >> ~/.config/nix/nix.confInitialiser un flake avec nix flake init
Section intitulée « Initialiser un flake avec nix flake init »La commande nix flake init génère un flake.nix de base dans le répertoire courant. Le répertoire doit être un dépôt Git, les flakes ne fonctionnent qu'avec des fichiers suivis par Git.
-
Créez un répertoire et initialisez Git :
Fenêtre de terminal mkdir mon-projet && cd mon-projetgit init -
Générez le flake de base :
Fenêtre de terminal nix flake initwrote: "/home/lab/mon-projet/flake.nix" -
Examinez le fichier généré :
Fenêtre de terminal cat flake.nix{description = "A very basic flake";inputs = {nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";};outputs = { self, nixpkgs }: {packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;packages.x86_64-linux.default = self.packages.x86_64-linux.hello;};} -
Ajoutez le fichier à Git (obligatoire pour que Nix le voie) :
Fenêtre de terminal git add flake.nix
Anatomie du fichier flake.nix
Section intitulée « Anatomie du fichier flake.nix »Un flake.nix est une expression Nix qui retourne un attribute set avec trois clés :
{ description = "Description du projet"; # texte libre
inputs = { # Les dépendances et leur source };
outputs = { self, ... }: # Ce que le flake produit { };}| Clé | Rôle | Obligatoire |
|---|---|---|
description | Texte affiché par nix flake metadata | Non (recommandé) |
inputs | Dépendances externes avec leur URL | Oui |
outputs | Fonction qui retourne packages, shells, apps… | Oui |
La clé outputs est une fonction dont le premier argument est toujours self (le flake lui-même), suivi d'un argument par input déclaré.
Déclarer ses dépendances dans inputs
Section intitulée « Déclarer ses dépendances dans inputs »Les inputs déclarent les sources dont votre flake dépend. La plus courante est nixpkgs.
Types de sources
Section intitulée « Types de sources »Chaque input s'écrit sous forme d'URL de flake, un format propre à Nix qui encode à la fois le protocole, l'emplacement et la révision voulue. Les quatre onglets ci-dessous couvrent les cas que vous rencontrerez : un dépôt GitHub (le cas courant), un dépôt Git quelconque pour du code interne, un chemin local pour un monorepo, et une archive pour du code qui n'est pas versionné dans Git. Retenez surtout le troisième segment de l'URL GitHub : il accepte une branche, un tag ou un commit, et c'est lui qui détermine ce que nix flake update ira chercher.
inputs = { # Branche spécifique nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
# Branche stable nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-24.11";
# Un outil tiers flake-utils.url = "github:numtide/flake-utils";};Format : github:propriétaire/dépôt/branche
inputs = { mon-outil.url = "git+https://gitlab.example.com/equipe/outil.git?ref=main";};Format : git+URL?ref=branche
inputs = { mon-lib.url = "path:./lib";};Utile pour un monorepo contenant plusieurs flakes.
inputs = { source.url = "https://example.com/archive.tar.gz"; source.flake = false;};flake = false indique que la source n'est pas elle-même un flake.
Dédupliquer les inputs transitifs avec follows
Section intitulée « Dédupliquer les inputs transitifs avec follows »Vos inputs ont eux-mêmes des inputs. home-manager déclare son propre nixpkgs, flake-utils déclare un input systems. Sans intervention, Nix verrouille deux révisions différentes de nixpkgs dans votre flake.lock : la vôtre et celle de home-manager. Vous vous retrouvez avec deux jeux de paquets côte à côte dans le store, et des incohérences difficiles à diagnostiquer quand les deux versions divergent. La directive follows redirige un input transitif vers un input que vous contrôlez :
inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager.url = "github:nix-community/home-manager"; # home-manager utilisera NOTRE nixpkgs au lieu du sien home-manager.inputs.nixpkgs.follows = "nixpkgs";};Le même mécanisme sert à restreindre la liste des architectures. flake-utils ne dépend pas de nixpkgs, son unique input s'appelle systems et pointe par défaut vers nix-systems/default (les quatre plateformes standards). En le faisant suivre un input à vous, vous limitez les outputs générés à ce qui vous intéresse :
inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils";
systems.url = "github:nix-systems/x86_64-linux"; flake-utils.inputs.systems.follows = "systems";};Sans follows, chaque input télécharge sa propre copie de ses dépendances. Avec follows, vous déduplicez, vous réduisez la taille du flake.lock et vous garantissez qu'une mise à jour de nixpkgs s'applique partout d'un coup.
Le fichier flake.lock : reproductibilité garantie
Section intitulée « Le fichier flake.lock : reproductibilité garantie »La première évaluation d'un flake génère automatiquement un flake.lock. Ce fichier JSON enregistre la révision exacte de chaque input :
{ "nodes": { "nixpkgs": { "locked": { "lastModified": 1775710090, "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=", "owner": "nixos", "repo": "nixpkgs", "rev": "4c1018dae018162ec878d42fec712642d214fdfa", "type": "github" }, "original": { "owner": "nixos", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "nixpkgs": "nixpkgs" } } }, "root": "root", "version": 7}Trois informations clés :
rev: le commit exact de nixpkgs utilisénarHash: le hash cryptographique du contenu, garantit l'intégritéoriginal: la source telle que déclarée dansinputs
Définir ses outputs : devShells, packages, apps
Section intitulée « Définir ses outputs : devShells, packages, apps »La fonction outputs retourne un attribute set dont les clés suivent une convention standardisée. Voici un flake complet avec les trois outputs les plus courants :
{ description = "Projet Python avec devShell et package";
inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; };
outputs = { self, nixpkgs }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; python = pkgs.python312; in { # Environnement de développement devShells.${system}.default = pkgs.mkShell { packages = [ (python.withPackages (ps: [ ps.requests ps.pytest ])) pkgs.curl pkgs.jq ]; shellHook = '' echo "Environnement Python pret" export PROJECT_NAME=mon-projet ''; };
# Paquet construisible packages.${system}.default = pkgs.writeShellScriptBin "hello-projet" '' echo "Bienvenue dans mon-projet !" '';
# Application exécutable apps.${system}.default = { type = "app"; program = "${self.packages.${system}.default}/bin/hello-projet"; }; };}Les types d'outputs standards
Section intitulée « Les types d'outputs standards »Les noms d'outputs ne sont pas libres : les commandes nix cherchent des chemins d'attributs précis. nix develop sans argument regarde devShells.<system>.default, nix build regarde packages.<system>.default. Un devShell rangé sous packages ne sera jamais trouvé. Lisez le tableau par sa colonne du milieu : elle indique quelle commande active quel output. Les deux lignes marquées « aucun » (overlays et nixosModules) sont des outputs consommés par d'autres flakes, jamais lancés directement.
| Output | Commande associée | Rôle |
|---|---|---|
devShells.<system>.default | nix develop | Environnement de développement |
packages.<system>.default | nix build | Paquet à construire |
apps.<system>.default | nix run | Application à exécuter |
overlays.default | aucun | Modification de nixpkgs |
nixosConfigurations.<nom> | nixos-rebuild | Configuration NixOS complète |
nixosModules.default | aucun | Module NixOS réutilisable |
Construire, exécuter et inspecter un flake
Section intitulée « Construire, exécuter et inspecter un flake »Visualiser la structure avec nix flake show
Section intitulée « Visualiser la structure avec nix flake show »nix flake show évalue les outputs et affiche leur arborescence. C'est la première commande à lancer sur un flake que vous ne connaissez pas : elle répond à « qu'est-ce que ce dépôt expose, et sous quel nom ». Les feuilles de l'arbre portent le type de chaque output (app, package, development environment), et c'est exactement ce nom d'attribut que vous passerez ensuite à nix build .#<nom>.
nix flake showgit+file:///home/lab/demo-flake-devshell├───apps│ └───x86_64-linux│ └───default: app├───devShells│ └───x86_64-linux│ └───default: development environment 'nix-shell'└───packages └───x86_64-linux └───default: package 'hello-projet'Consulter les métadonnées avec nix flake metadata
Section intitulée « Consulter les métadonnées avec nix flake metadata »Là où nix flake show décrit les sorties, nix flake metadata décrit les entrées. La ligne à surveiller est l'arbre Inputs : il affiche la révision réellement verrouillée et sa date, pas la branche demandée. C'est la façon la plus rapide de répondre à « depuis combien de temps ce projet n'a pas mis à jour nixpkgs », sans ouvrir le flake.lock. Le champ Path donne le chemin du store où la copie du dépôt a été importée.
nix flake metadataResolved URL: git+file:///home/lab/demo-flake-devshellDescription: Projet Python avec devShell et packagePath: /nix/store/d9pvgm44r0dnqlg8whgdpzd7q73ihayj-sourceInputs:└───nixpkgs: github:nixos/nixpkgs/4c1018dae018162ec878d42fec712642d214fdfa (2026-04-09)Entrer dans l'environnement de développement
Section intitulée « Entrer dans l'environnement de développement »nix develop construit toutes les dépendances du devShell puis ouvre un sous-shell dont le PATH pointe vers le store. Le premier lancement peut prendre plusieurs minutes s'il faut télécharger ou compiler ; les suivants sont immédiats grâce au cache du store. Vous restez dans votre shell habituel, seules les variables d'environnement changent : sortez avec exit ou Ctrl+D et votre PATH d'origine revient.
nix developEnvironnement Python pretLe shellHook s'exécute et toutes les dépendances sont disponibles :
python3 --version && echo $PROJECT_NAMEPython 3.12.13mon-projetConstruire un paquet
Section intitulée « Construire un paquet »nix buildbuilding '/nix/store/8s05glip2sx5zb5gj1n6l0r2ggjn7h96-hello-projet.drv'...Le résultat est un lien symbolique ./result pointant vers le store :
ls -la resultresult -> /nix/store/10s5j3mfdg22k1597x580qrhprnzcjwb-hello-projet./result/bin/hello-projetBienvenue dans mon-projet !Exécuter directement avec nix run
Section intitulée « Exécuter directement avec nix run »nix runBienvenue dans mon-projet !nix run combine nix build + exécution du binaire par défaut en une seule commande.
Vérifier la validité du flake
Section intitulée « Vérifier la validité du flake »nix flake checkchecking flake output 'devShells'...checking derivation devShells.x86_64-linux.default...checking flake output 'packages'...checking derivation packages.x86_64-linux.default...checking flake output 'apps'...checking app 'apps.x86_64-linux.default'...all checks passed!nix flake check évalue toutes les dérivations et vérifie la conformité des outputs. Indispensable dans une pipeline CI.
Mettre à jour les dépendances
Section intitulée « Mettre à jour les dépendances »Pour mettre à jour tous les inputs :
nix flake updatePour mettre à jour un seul input :
nix flake update nixpkgsAprès la mise à jour, vérifiez le diff de flake.lock avec git diff flake.lock avant de commiter.
Convertir un projet shell.nix existant vers les flakes
Section intitulée « Convertir un projet shell.nix existant vers les flakes »Si vous avez un shell.nix classique qui utilise <nixpkgs>, voici comment le migrer vers un flake.
Avant : shell.nix classique
Section intitulée « Avant : shell.nix classique »{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell { packages = with pkgs; [ python312 python312Packages.requests git curl ];
shellHook = '' echo "Environnement dev classique" '';}Ce fichier fonctionne avec nix-shell, mais les versions dépendent du channel actif sur chaque machine.
Après : flake.nix
Section intitulée « Après : flake.nix »La liste de paquets est reprise telle quelle, y compris le with pkgs;. Trois choses seulement changent : la source de pkgs devient un input verrouillé au lieu de <nixpkgs>, le mkShell est rangé sous l'attribut devShells.<system>.default attendu par nix develop, et le system est écrit en dur. Le shellHook conserve la même syntaxe, ce qui rend la migration mécanique sur la plupart des projets.
{ description = "Projet migre de shell.nix vers flake";
inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; };
outputs = { self, nixpkgs }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; in { devShells.${system}.default = pkgs.mkShell { packages = with pkgs; [ python312 python312Packages.requests git curl ];
shellHook = '' echo "Environnement dev (flake, nixos-24.11)" ''; }; };}Les différences concrètes
Section intitulée « Les différences concrètes »| Aspect | shell.nix | flake.nix |
|---|---|---|
| Source nixpkgs | <nixpkgs> (channel local) | github:nixos/nixpkgs/nixos-24.11 (verrouillé) |
| Versions obtenues | Varient selon la machine | Identiques partout |
| Commande d'entrée | nix-shell | nix develop |
| Verrouillage | Aucun (ou fetchTarball manuel) | Automatique (flake.lock) |
Le résultat avec nixos-24.11 verrouillé :
nix develop --command bash -c "python3 --version && git --version"Python 3.12.8git version 2.47.2Tandis qu'avec nixos-unstable, les mêmes paquets donnent Python 3.12.13 et git 2.53.0.
Supporter plusieurs architectures
Section intitulée « Supporter plusieurs architectures »Par défaut, un flake cible un seul system (typiquement "x86_64-linux"). Pour supporter Linux et macOS sur x86_64 et ARM, utilisez flake-utils :
Avec flake-utils
Section intitulée « Avec flake-utils »{ description = "Flake multi-architecture";
inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; };
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; in { devShells.default = pkgs.mkShell { packages = [ pkgs.jq pkgs.curl ]; };
packages.default = pkgs.hello; } );}eachDefaultSystem itère automatiquement sur les quatre systèmes standards :
nix flake show├───devShells│ ├───aarch64-darwin│ │ └───default omitted (use '--all-systems' to show)│ ├───aarch64-linux│ │ └───default omitted (use '--all-systems' to show)│ ├───x86_64-darwin│ │ └───default omitted (use '--all-systems' to show)│ └───x86_64-linux│ └───default: development environment 'nix-shell'└───packages ├───aarch64-darwin │ └───default omitted (use '--all-systems' to show) ├───aarch64-linux │ └───default omitted (use '--all-systems' to show) ├───x86_64-darwin │ └───default omitted (use '--all-systems' to show) └───x86_64-linux └───default: package 'hello-2.12.3'Sans flake-utils
Section intitulée « Sans flake-utils »Si vous préférez éviter la dépendance à flake-utils, utilisez genAttrs de nixpkgs :
outputs = { self, nixpkgs }: let supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" ]; forAllSystems = nixpkgs.lib.genAttrs supportedSystems; pkgsFor = system: nixpkgs.legacyPackages.${system}; in { packages = forAllSystems (system: { default = (pkgsFor system).hello; });
devShells = forAllSystems (system: { default = (pkgsFor system).mkShell { packages = [ (pkgsFor system).hello ]; }; }); };Intégrer les flakes avec direnv
Section intitulée « Intégrer les flakes avec direnv »direnv active automatiquement l'environnement du flake quand vous entrez dans le répertoire du projet. Plus besoin de taper nix develop manuellement.
-
Installez direnv et nix-direnv :
Fenêtre de terminal nix profile add nixpkgs#direnv nixpkgs#nix-direnvSur une installation antérieure à Nix 2.30, la sous-commande s'appelle
nix profile install. Elle reste acceptée comme alias déprécié. -
Configurez votre shell (ajoutez à
~/.bashrcou~/.zshrc) :Fenêtre de terminal eval "$(direnv hook bash)" # ou hook zshsource $HOME/.nix-profile/share/nix-direnv/direnvrc -
Créez un fichier
.envrcà la racine du projet :Fenêtre de terminal echo "use flake" > .envrcdirenv allow -
À chaque
cddans le projet, l'environnement se charge automatiquement.
Structure recommandée d'un projet avec flakes
Section intitulée « Structure recommandée d'un projet avec flakes »Pour un projet réel, organisez vos fichiers Nix dans un sous-répertoire :
Répertoiremon-projet/
- flake.nix Point d'entrée principal
- flake.lock Versions verrouillées (généré)
- .envrc Pour direnv (
use flake) Répertoirenix/
Répertoiredevshells/
- default.nix Définition du devShell
Répertoirepackages/
- default.nix Définitions de paquets
Répertoireoverlays/
- default.nix Overlays personnalisés
Répertoiresrc/ Code source de l'application
- …
Le flake.nix importe ensuite ces fichiers :
{ description = "Projet organisé";
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
outputs = { self, nixpkgs }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; in { devShells.${system}.default = import ./nix/devshells/default.nix { inherit pkgs; }; packages.${system}.default = import ./nix/packages/default.nix { inherit pkgs; }; };}Comparaison flakes et channels
Section intitulée « Comparaison flakes et channels »Ce tableau résume ce que change le passage aux flakes sur un projet existant. Les deux lignes qui pèsent le plus dans une décision d'équipe sont Verrouillage et Évaluation : la première explique pourquoi la CI et le poste développeur finissent par diverger avec les channels, la seconde pourquoi un flake refuse de lire un fichier absent de Git. Les autres lignes décrivent du confort, celles-là décrivent des pannes que vous éviterez.
| Aspect | Channels (classique) | Flakes |
|---|---|---|
| Verrouillage | Manuel (fetchTarball + hash) | Automatique (flake.lock) |
| Reproductibilité | Difficile à garantir | Garantie par défaut |
| Structure | Libre (convention par projet) | Standardisée (inputs/outputs) |
| Composition | Complexe (imports manuels) | Native (follows, multi-inputs) |
| Évaluation | Impure (accès au système de fichiers) | Pure (hermétique) |
| Commandes | nix-env, nix-shell, nix-build | nix develop, nix build, nix run |
| Mise à jour | nix-channel --update (global) | nix flake update (par projet) |
| CI/CD | Configuration manuelle | Structure prédictible |
Dépannage courant
Section intitulée « Dépannage courant »Quatre des six erreurs ci-dessous ont la même origine : l'évaluation pure. Un flake ne voit que les fichiers suivis par Git, un fichier créé mais jamais ajouté à l'index n'existe pas pour Nix, même s'il est bien sur le disque. Le réflexe à acquérir devant un message qui parle d'un chemin introuvable est donc git add, pas la recherche d'une erreur de syntaxe. L'avertissement Git tree is dirty fait exception : il est informatif, la construction aboutit quand même, il signale simplement que le résultat ne correspond à aucun commit et n'est donc pas reproductible ailleurs.
| Symptôme | Cause probable | Solution |
|---|---|---|
error: experimental feature 'flakes' is disabled | Flakes non activés | Ajouter experimental-features = nix-command flakes dans ~/.config/nix/nix.conf |
error: getting status of '/flake.nix': No such file or directory | Fichier non suivi par Git | git add flake.nix |
error: path '/nix/store/…' is not valid | Fichier manquant dans Git | git add <fichier-manquant> |
warning: Git tree '…' is dirty | Modifications non commitées | git add -A && git commit (ou ignorer l'avertissement) |
| Build échoue en CI mais pas en local | flake.lock absent ou pas à jour | git add flake.lock && git commit |
--update-input affiche un warning | Syntaxe dépréciée | Utiliser nix flake update <input> |
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Un flake =
flake.nix(déclaration) +flake.lock(verrouillage) dans un dépôt Git - Les inputs déclarent les sources ;
followsévite la duplication - Les outputs produisent des devShells, packages, apps, avec une convention standardisée
nix developremplacenix-shell,nix buildremplacenix-build,nix runexécute directementnix flake checkvalide la structure, indispensable en CInix flake updatemet à jour les inputs ; le diff deflake.locksert de revue- flake-utils simplifie le support multi-architecture
- direnv + nix-direnv activent l'environnement automatiquement
Pour aller plus loin
Section intitulée « Pour aller plus loin »- NixOS : le système déclaratif : Découvrez NixOS, le système d'exploitation entièrement déclaratif construit sur Nix et les flakes.