Un shell.nix qui importe <nixpkgs> utilise la version du channel
installé sur votre machine. Rien ne garantit que votre collègue a le
même. Résultat : Python 3.12.14 chez vous, Python 3.12.8 chez lui. Ce
guide montre comment modulariser vos fichiers Nix et figer les
versions pour que tout le monde travaille avec exactement les mêmes
paquets. Les sorties ont été relevées sur Nix 2.35.2.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Séparer la configuration dans plusieurs fichiers avec
import - Passer des arguments entre fichiers avec
inherit - Fusionner des configurations avec l'opérateur
// - Figer une version de nixpkgs avec
fetchTarballet un hash - Trouver le sha256 d'une archive avec
nix-prefetch-url - Comprendre pourquoi les flakes ont été créés
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Dès que votre projet Nix dépasse quelques lignes, vous rencontrez ces situations, qui relèvent soit de la modularisation, soit du pinning :
- votre
shell.nixmélange configuration, paquets Python, outils CLI et variables d'environnement dans un seul fichier qui grandit vite, - vous voulez réutiliser la même liste de paquets Python dans plusieurs projets sans copier-coller,
- deux développeurs ont des résultats différents parce que leurs channels Nix ne pointent pas vers la même version de nixpkgs,
- vous avez besoin d'une version précise d'un outil pour reproduire un bug signalé en CI.
La modularisation et le pinning résolvent chacun de ces problèmes.
Séparer la configuration avec import
Section intitulée « Séparer la configuration avec import »import charge et évalue un fichier .nix. Si le fichier contient un
attrset, il retourne cet attrset. Si le fichier contient une
fonction, il retourne cette fonction (qu'il faut ensuite appeler).
Extraire les variables dans config.nix
Section intitulée « Extraire les variables dans config.nix »Le premier fichier extrait ne contient que des valeurs : nom, version, mode debug et port. C'est le seul endroit à modifier pour adapter le projet.
{ projectName = "mon-api"; projectVersion = "2.0.0"; debugMode = true; listenPort = 5000;}Ce fichier est un simple attrset, import ./config.nix retourne
directement { projectName = "mon-api"; ... }, sans argument à passer ni
fonction à appeler.
Extraire les paquets Python dans python-packages.nix
Section intitulée « Extraire les paquets Python dans python-packages.nix »Le deuxième fichier est plus riche : il construit l'environnement Python en
ajoutant les outils de développement seulement quand debugMode est actif.
Il a donc besoin de pkgs et de la configuration.
{ pkgs, config }:
let basePackages = ps: [ ps.requests ps.flask ];
devPackages = ps: [ ps.pytest ps.black ];inpkgs.python312.withPackages (ps: basePackages ps ++ (if config.debugMode then devPackages ps else []))Ce fichier est une fonction qui attend pkgs et config. Il faut
l'appeler avec les arguments : import ./python-packages.nix { inherit pkgs config; }.
Assembler dans shell.nix
Section intitulée « Assembler dans shell.nix »Le point d'entrée importe les deux fichiers et compose l'environnement
final. Notez l'appel de python-packages.nix avec inherit pkgs config : c'est
ce qui transmet les deux arguments attendus.
{ pkgs ? import <nixpkgs> {} }:
let config = import ./config.nix; pythonEnv = import ./python-packages.nix { inherit pkgs config; };
debugTools = if config.debugMode then with pkgs; [ htop ncdu ] else [];inpkgs.mkShell { name = "${config.projectName}-dev";
packages = with pkgs; [ pythonEnv git ripgrep ] ++ debugTools;
PROJECT_NAME = config.projectName; PROJECT_VERSION = config.projectVersion; LISTEN_PORT = toString config.listenPort;}nix-shell --run "echo \$PROJECT_NAME \$PROJECT_VERSION \$LISTEN_PORT && which htop"mon-api 2.0.0 5000/nix/store/w31kqb23ydg3fli2b8dsy0gl0ily15bm-htop-3.5.3/bin/htopLa configuration est centralisée dans config.nix, un seul endroit à
modifier pour changer le nom du projet, la version ou le mode debug.
Fusionner des configurations avec //
Section intitulée « Fusionner des configurations avec // »L'opérateur // fusionne deux attrsets. Les clés du côté droit écrasent
celles du côté gauche. C'est idéal pour un système de surcharges :
defaults.nix, valeurs par défaut
Section intitulée « defaults.nix, valeurs par défaut »Ce fichier porte la configuration versionnée du projet, celle que toute l'équipe partage. Il doit rester exhaustif : chaque clé susceptible d'être surchargée doit y figurer avec une valeur sûre, ici debugMode = false. Une clé absente de defaults.nix mais consommée ailleurs provoquera une erreur d'attribut manquant dès qu'un développeur travaillera sans overrides.nix.
{ projectName = "mon-api"; projectVersion = "2.0.0"; debugMode = false; listenPort = 5000;}overrides.nix, surcharges locales
Section intitulée « overrides.nix, surcharges locales »Ce second fichier ne contient que les écarts par rapport aux valeurs par défaut, jamais la configuration complète. Deux clés suffisent ici, les autres sont héritées telles quelles. La fusion est superficielle : // remplace la valeur d'une clé en bloc, il ne fusionne pas récursivement deux attrsets imbriqués. Si debugMode était un attrset plutôt qu'un booléen, redéfinir une seule de ses sous-clés effacerait toutes les autres.
{ debugMode = true; listenPort = 8080;}Fusion dans shell.nix
Section intitulée « Fusion dans shell.nix »La fusion tient en une ligne : defaults // overrides. Les commentaires
indiquent pour chaque clé si elle vient des surcharges ou des valeurs par
défaut.
let defaults = import ./defaults.nix; overrides = import ./overrides.nix; config = defaults // overrides;in# config.debugMode = true (surchargé)# config.listenPort = 8080 (surchargé)# config.projectName = "mon-api" (hérité)nix-shell --run "echo \$LISTEN_PORT"8080Le port est bien 8080 (overrides) au lieu de 5000 (defaults), et
projectName reste "mon-api" car il n'est pas surchargé : la fusion a
appliqué la priorité du côté droit.
Cas d'usage : environnements multiples
Section intitulée « Cas d'usage : environnements multiples »Créez un fichier par environnement : chacun ne porte que ses écarts, et
defaults.nix reste la base commune.
mon-projet/├── shell.nix├── defaults.nix├── overrides-dev.nix # debugMode=true, port 8080├── overrides-staging.nix # debugMode=false, port 5000└── python-packages.nixSélectionnez l'environnement depuis shell.nix :
config = defaults // (import ./overrides-dev.nix);Créer des fonctions utilitaires réutilisables
Section intitulée « Créer des fonctions utilitaires réutilisables »Quand une logique revient dans plusieurs projets, extrayez-la dans un fichier dédié, sous forme de fonction :
config: { PROJECT_NAME = config.projectName; PROJECT_VERSION = config.projectVersion; LISTEN_PORT = toString config.listenPort; DEBUG = if config.debugMode then "1" else "0";}let config = import ./config.nix; mkEnvVars = import ./lib/mk-env-vars.nix; envVars = mkEnvVars config;inpkgs.mkShell ({ packages = [ ... ];} // envVars)L'opérateur // fusionne l'attrset de mkShell avec les variables
d'environnement, elles sont automatiquement exportées.
Pourquoi figer la version de nixpkgs
Section intitulée « Pourquoi figer la version de nixpkgs »Par défaut, import <nixpkgs> {} utilise le channel système. Ce channel
change à chaque mise à jour. Conséquence, mesurée entre le nixpkgs-unstable
du 29 septembre 2026 et la branche nixos-24.11 :
| Machine A (channel à jour) | Machine B (channel ancien) |
|---|---|
| Python 3.12.14 | Python 3.12.8 |
| git 2.55.0 | git 2.47.2 |
Quelques mois d'écart dans les channels produisent des différences visibles. Pour un environnement reproductible, il faut figer la source.
Méthode 1 : fetchTarball avec un sha256
Section intitulée « Méthode 1 : fetchTarball avec un sha256 »Remplacez import <nixpkgs> {} par un import d'archive fixe, accompagnée
de son hash : Nix télécharge l'archive, vérifie le hash, puis évalue cette
version de nixpkgs.
{ pkgs ? import (fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/nixos-24.11.tar.gz"; sha256 = "sha256:1s2gr5rcyqvpr58vxdcb095mdhblij9bfzaximrva2243aal3dgx"; }) {}}:
pkgs.mkShell { packages = [ pkgs.python312 pkgs.git ];}Maintenant, peu importe le channel installé sur la machine :
nix-shell --run "python3 --version && git --version"Python 3.12.8git version 2.47.2Les versions obtenues sont celles de l'archive téléchargée, et non plus celles du channel local.
Méthode 2 : figer sur un commit précis
Section intitulée « Méthode 2 : figer sur un commit précis »Pour encore plus de précision, utilisez un commit spécifique de nixpkgs,
avec le hash de son archive calculé par nix-prefetch-url --unpack :
{ pkgs ? import (fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/4c1018dae018162ec878d42fec712642d214fdfa.tar.gz"; sha256 = "sha256:18ggs7jwmpi58k7xza4axy3cjs17c596ihq5y70h6sryz2hypgba"; }) {}}:
pkgs.mkShell { packages = [ pkgs.python312 ];}Un commit ne change jamais, c'est la garantie la plus forte de reproductibilité sans flakes.
Trouver le sha256 d'une archive nixpkgs
Section intitulée « Trouver le sha256 d'une archive nixpkgs »Deux méthodes donnent la même empreinte, sous deux encodages
différents que fetchTarball accepte l'un comme l'autre.
Méthode 1 : laisser Nix calculer le hash
Section intitulée « Méthode 1 : laisser Nix calculer le hash »Mettez un sha256 vide et lancez nix-shell. L'erreur affiche le
bon hash, au format SRI (sha256- suivi de base64) :
sha256 = ""; # ← intentionnellement videwarning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='...error: NAR hash mismatch in input 'https://github.com/NixOS/nixpkgs/archive/nixos-24.11.tar.gz?narHash=sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3D', expected 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' but got 'sha256-/bVBlRpECLVzjV19t5KMdMFWSwKLtb5RyXdjz3LJT+g='Copiez la valeur qui suit but got dans votre fichier : sha256 = "sha256-/bVBlRpECLVzjV19t5KMdMFWSwKLtb5RyXdjz3LJT+g="; fonctionne tel quel.
Méthode 2 : nix-prefetch-url
Section intitulée « Méthode 2 : nix-prefetch-url »La commande télécharge et dépaquette l'archive, l'ajoute au store et affiche son hash :
nix-prefetch-url --unpack https://github.com/NixOS/nixpkgs/archive/nixos-24.11.tar.gzpath is '/nix/store/yb6a9i6qlzvhdz08d82ay7107z8qrnb3-nixos-24.11.tar.gz'1s2gr5rcyqvpr58vxdcb095mdhblij9bfzaximrva2243aal3dgxPréfixez avec sha256: dans le fichier Nix.
Trouver un paquet dans une version précise
Section intitulée « Trouver un paquet dans une version précise »Vérifier la version dans un nixpkgs donné
Section intitulée « Vérifier la version dans un nixpkgs donné »nix eval interroge l'arbre de paquets sans rien construire ni télécharger : la réponse arrive en quelques secondes, ce qui en fait le contrôle à faire avant de figer un hash. Le drapeau --impure est indispensable ici, car <nixpkgs> est résolu via la variable NIX_PATH de votre machine, une entrée extérieure que le mode pur refuse par principe. La valeur retournée est donc celle de votre channel, pas celle de l'archive que vous vous apprêtez à figer : pour interroger une version précise, remplacez <nixpkgs> par le même fetchTarball que dans votre shell.nix.
nix eval --impure --expr '(import <nixpkgs> {}).python312.version'"3.12.14"Nixhub.io : chercher par version
Section intitulée « Nixhub.io : chercher par version »Le site Nixhub.io permet de rechercher quelle version de nixpkgs contient une version précise d'un paquet. Si vous avez besoin de Python 3.12.8 exactement, Nixhub vous indique quel commit ou quelle branche utiliser.
Structure recommandée pour un projet multi-fichiers
Section intitulée « Structure recommandée pour un projet multi-fichiers »Les fichiers vus dans ce guide se rangent selon leur responsabilité : la configuration, les paquets, les utilitaires, et le point d'entrée qui les assemble.
mon-projet/├── shell.nix # point d'entrée, import des modules├── defaults.nix # configuration par défaut├── overrides.nix # surcharges locales (gitignored si besoin)├── python-packages.nix # paquets Python paramétrés└── lib/ └── mk-env-vars.nix # fonctions utilitairesPrincipes :
- Un fichier par responsabilité (configuration, paquets, utilitaires)
defaults.nixversionné,overrides.nixoptionnel et local- Les fonctions utilitaires dans
lib/ - Le pinning dans
shell.nix(ou dans unnixpkgs.nixdédié)
Limites du pinning classique : pourquoi les flakes existent
Section intitulée « Limites du pinning classique : pourquoi les flakes existent »Le pinning avec fetchTarball fonctionne, mais présente des inconvénients
qui se paient à chaque mise à jour et dès que plusieurs sources doivent
être combinées :
| Limite | Impact |
|---|---|
| Hash manuel | Vous devez chercher et coller le sha256 à chaque mise à jour |
| Pas de lock partagé | Chaque développeur doit avoir le bon hash dans le fichier |
| Mise à jour fastidieuse | Changer de version = modifier l'URL + recalculer le hash |
| Pas de composition | Combiner plusieurs sources (overlay, modules) est complexe |
Les flakes résolvent ces problèmes avec :
- un fichier
flake.lockautomatique et versionnable, - une commande
nix flake updatepour mettre à jour, - une composition native de plusieurs inputs.
Le pinning classique reste parfaitement valide pour les projets simples ou les environnements sans flakes. Pour les projets d'équipe, les flakes sont la solution recommandée.
Dépannage courant
Section intitulée « Dépannage courant »Deux familles d'erreurs se distinguent dans ce tableau, et elles ne se diagnostiquent pas de la même façon. Les trois premières lignes concernent la source des paquets : l'évaluation n'a même pas commencé. Les trois suivantes concernent le langage Nix lui-même, et le message d'erreur cite alors le fichier et la ligne fautive. Réflexe utile dans les deux cas : relancer avec nix-shell --show-trace, qui déplie la pile d'évaluation et désigne le fichier importé réellement responsable, souvent différent de celui que vous éditiez.
| Symptôme | Cause probable | Solution |
|---|---|---|
hash mismatch | sha256 incorrect ou archive modifiée | Recalculer avec nix-prefetch-url --unpack |
cannot look up '<nixpkgs>' in pure evaluation mode | nix eval ou flake en mode pur | Ajouter --impure ou figer avec fetchTarball |
file 'nixpkgs' was not found in the Nix search path | Aucun channel configuré | nix-channel --update, ou figer avec fetchTarball |
import ./file.nix échoue | Chemin relatif incorrect | Vérifier que le fichier existe au bon endroit |
Argument manquant dans import | Fichier = fonction non appelée | Passer les arguments : import ./f.nix { inherit pkgs; } |
Attribut absent après // | Clé non définie dans defaults | Vérifier les deux attrsets avant fusion |
À retenir
Section intitulée « À retenir »import ./file.nixcharge un fichier Nix, vous pouvez séparer configuration, paquets et utilitaires.- Si le fichier est une fonction, il faut l'appeler avec ses arguments :
import ./f.nix { inherit pkgs; }. - L'opérateur
//fusionne deux attrsets, idéal pour un système defaults + overrides. fetchTarballsur l'archive d'un commit, avec son sha256, fige la version de nixpkgs : plus aucune dérive entre machines.- Le hash se récupère avec
nix-prefetch-url --unpackou en laissant Nix le calculer (sha256 vide). - Une URL de branche (
nixos-24.11) n'est pas un pin durable : la branche avance et le hash finit par ne plus correspondre. Seul un commit est immuable. - Pour les projets d'équipe, les flakes remplacent avantageusement le pinning manuel grâce au
flake.lock.
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)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Flakes
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications