Aller au contenu
Administration Linux medium

Comprendre et utiliser les flakes Nix

20 min de lecture

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.

  • Créer un flake avec nix flake init et 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.nix classique vers les flakes
  • Supporter plusieurs architectures avec flake-utils

Les channels Nix classiques (guide précédent) 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 dans flake.lock
  • Vous composez plusieurs sources (nixpkgs stable, un overlay privé, un outil depuis GitHub) dans un seul fichier déclaratif

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 :

Fenêtre de terminal
grep experimental-features ~/.config/nix/nix.conf 2>/dev/null || \
grep experimental-features /etc/nix/nix.conf 2>/dev/null
experimental-features = nix-command flakes

Si la ligne est absente :

Fenêtre de terminal
mkdir -p ~/.config/nix
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf

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.

  1. Créez un répertoire et initialisez Git :

    Fenêtre de terminal
    mkdir mon-projet && cd mon-projet
    git init
  2. Générez le flake de base :

    Fenêtre de terminal
    nix flake init
    wrote: "/home/lab/mon-projet/flake.nix"
  3. 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;
    };
    }
  4. Ajoutez le fichier à Git (obligatoire pour que Nix le voie) :

    Fenêtre de terminal
    git add 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ôleObligatoire
descriptionTexte affiché par nix flake metadataNon (recommandé)
inputsDépendances externes avec leur URLOui
outputsFonction 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é.

Les inputs déclarent les sources dont votre flake dépend. La plus courante est nixpkgs.

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

Partager un même nixpkgs entre inputs avec follows

Section intitulée « Partager un même nixpkgs entre inputs avec follows »

Quand un input (comme flake-utils) dépend lui-même de nixpkgs, vous pouvez forcer l’utilisation de votre version :

inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
# Force flake-utils à utiliser notre nixpkgs
flake-utils.inputs.systems.follows = "systems";
systems.url = "github:nix-systems/x86_64-linux";
};

Sans follows, chaque input télécharge sa propre copie de ses dépendances. Avec follows, vous déduplicez et garantissez la cohérence.

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 dans inputs

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";
};
};
}
OutputCommande associéeRôle
devShells.<system>.defaultnix developEnvironnement de développement
packages.<system>.defaultnix buildPaquet à construire
apps.<system>.defaultnix runApplication à exécuter
overlays.defaultModification de nixpkgs
nixosConfigurations.<nom>nixos-rebuildConfiguration NixOS complète
nixosModules.defaultModule NixOS réutilisable
Fenêtre de terminal
nix flake show
git+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 »
Fenêtre de terminal
nix flake metadata
Resolved URL: git+file:///home/lab/demo-flake-devshell
Description: Projet Python avec devShell et package
Path: /nix/store/d9pvgm44r0dnqlg8whgdpzd7q73ihayj-source
Inputs:
└───nixpkgs: github:nixos/nixpkgs/4c1018dae018162ec878d42fec712642d214fdfa (2026-04-09)
Fenêtre de terminal
nix develop
Environnement Python pret

Le shellHook s’exécute et toutes les dépendances sont disponibles :

Fenêtre de terminal
python3 --version && echo $PROJECT_NAME
Python 3.12.13
mon-projet
Fenêtre de terminal
nix build
building '/nix/store/8s05glip2sx5zb5gj1n6l0r2ggjn7h96-hello-projet.drv'...

Le résultat est un lien symbolique ./result pointant vers le store :

Fenêtre de terminal
ls -la result
result -> /nix/store/10s5j3mfdg22k1597x580qrhprnzcjwb-hello-projet
Fenêtre de terminal
./result/bin/hello-projet
Bienvenue dans mon-projet !
Fenêtre de terminal
nix run
Bienvenue dans mon-projet !

nix run combine nix build + exécution du binaire par défaut en une seule commande.

Fenêtre de terminal
nix flake check
checking 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.

Pour mettre à jour tous les inputs :

Fenêtre de terminal
nix flake update

Pour mettre à jour un seul input :

Fenêtre de terminal
nix flake update nixpkgs

Aprè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.

shell.nix
{ 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.

{
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)"
'';
};
};
}
Aspectshell.nixflake.nix
Source nixpkgs<nixpkgs> (channel local)github:nixos/nixpkgs/nixos-24.11 (verrouillé)
Versions obtenuesVarient selon la machineIdentiques partout
Commande d’entréenix-shellnix develop
VerrouillageAucun (ou fetchTarball manuel)Automatique (flake.lock)

Le résultat avec nixos-24.11 verrouillé :

Fenêtre de terminal
nix develop --command bash -c "python3 --version && git --version"
Python 3.12.8
git version 2.47.2

Tandis qu’avec nixos-unstable, les mêmes paquets donnent Python 3.12.13 et git 2.53.0.

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 :

{
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 :

Fenêtre de terminal
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'

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 ];
};
});
};

direnv active automatiquement l’environnement du flake quand vous entrez dans le répertoire du projet. Plus besoin de taper nix develop manuellement.

  1. Installez direnv et nix-direnv :

    Fenêtre de terminal
    nix profile install nixpkgs#direnv nixpkgs#nix-direnv
  2. Configurez votre shell (ajoutez à ~/.bashrc ou ~/.zshrc) :

    Fenêtre de terminal
    eval "$(direnv hook bash)" # ou hook zsh
    source $HOME/.nix-profile/share/nix-direnv/direnvrc
  3. Créez un fichier .envrc à la racine du projet :

    Fenêtre de terminal
    echo "use flake" > .envrc
    direnv allow
  4. À chaque cd dans le projet, l’environnement se charge automatiquement.

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; };
};
}
AspectChannels (classique)Flakes
VerrouillageManuel (fetchTarball + hash)Automatique (flake.lock)
ReproductibilitéDifficile à garantirGarantie par défaut
StructureLibre (convention par projet)Standardisée (inputs/outputs)
CompositionComplexe (imports manuels)Native (follows, multi-inputs)
ÉvaluationImpure (accès au système de fichiers)Pure (hermétique)
Commandesnix-env, nix-shell, nix-buildnix develop, nix build, nix run
Mise à journix-channel --update (global)nix flake update (par projet)
CI/CDConfiguration manuelleStructure prédictible
SymptômeCause probableSolution
error: experimental feature 'flakes' is disabledFlakes non activésAjouter experimental-features = nix-command flakes dans ~/.config/nix/nix.conf
error: getting status of '/flake.nix': No such file or directoryFichier non suivi par Gitgit add flake.nix
error: path '/nix/store/…' is not validFichier manquant dans Gitgit add <fichier-manquant>
warning: Git tree '…' is dirtyModifications non commitéesgit add -A && git commit (ou ignorer l’avertissement)
Build échoue en CI mais pas en localflake.lock absent ou pas à jourgit add flake.lock && git commit
--update-input affiche un warningSyntaxe dépréciéeUtiliser nix flake update <input>
  • 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 develop remplace nix-shell, nix build remplace nix-build, nix run exécute directement
  • nix flake check valide la structure — indispensable en CI
  • nix flake update met à jour les inputs ; le diff de flake.lock sert de revue
  • flake-utils simplifie le support multi-architecture
  • direnv + nix-direnv activent l’environnement automatiquement

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn