Aller au contenu
Administration Linux medium

Nix store, profils et générations : comprendre le modèle de données

16 min de lecture

Le store Nix (/nix/store/) est le répertoire unique où Nix dépose tout ce qu'il installe : chaque paquet y vit dans un sous-répertoire identifié par un hash cryptographique. Deux installations identiques produisent exactement le même chemin, deux versions différentes cohabitent sans conflit. Au-dessus, les profils exposent les binaires via des liens symboliques, et les générations gardent un historique complet qui permet un rollback instantané. Ce modèle élimine les conflits de dépendances, rend chaque opération atomique et permet de récupérer l'espace disque à la demande.

  • Comprendre la structure du store Nix et le rôle du hash dans chaque chemin
  • Explorer un paquet dans le store et sa closure (arbre de dépendances complet)
  • Manipuler les profils utilisateur et voir comment les binaires sont exposés
  • Créer, lister et basculer entre les générations
  • Effectuer un rollback instantané vers un état précédent
  • Libérer de l'espace avec le garbage collector et l'optimisation du store

Quand vous installez un paquet avec nix profile add, vous ne touchez pas aux répertoires système classiques (/usr/bin, /usr/lib). Le paquet atterrit dans le store, et un lien symbolique apparaît dans votre profil. Comprendre ce mécanisme est indispensable pour :

  • diagnostiquer pourquoi un binaire n'est pas trouvé après installation,
  • libérer de l'espace disque sans casser les paquets actifs,
  • revenir en arrière après une mise à jour ratée,
  • partager un environnement reproductible entre machines,
  • comprendre les closures pour optimiser la taille de vos déploiements.

Le store remplace à lui seul /usr/bin, /usr/lib et /usr/share : Nix n'écrit jamais dans ces répertoires. Il est monté en lecture seule et appartient à root, y compris quand vous installez un paquet en tant qu'utilisateur normal. Les quatre sous-sections détaillent sa convention de nommage, le contenu d'un paquet, la raison de son immuabilité et la façon dont sa taille évolue.

Chaque élément du store Nix vit dans un chemin de la forme :

/nix/store/<hash-32-caractères>-<nom>-<version>

Le hash est calculé à partir de toutes les entrées de la construction : code source, dépendances, flags de compilation, patches. Si l'une de ces entrées change, le hash change, et un nouveau chemin est créé.

Fenêtre de terminal
ls /nix/store/ | grep ripgrep
Résultat
922crn2k3v8yaqk7anps80hba919lnds-ripgrep-15.1.0

Chaque répertoire du store suit une structure FHS-like (mais isolée) :

Fenêtre de terminal
ls -la /nix/store/922crn2k3v8yaqk7anps80hba919lnds-ripgrep-15.1.0/
Résultat
dr-xr-xr-x 4 root root 4096 Jan 1 1970 .
dr-xr-xr-x 2 root root 4096 Jan 1 1970 bin
dr-xr-xr-x 6 root root 4096 Jan 1 1970 share

Le binaire rg est dans bin/, les pages man dans share/. Mais contrairement à un paquet Debian ou RPM, rien n'est copié dans /usr/bin, le paquet vit exclusivement dans le store.

Les répertoires du store sont en lecture seule (dr-xr-xr-x). Une fois construit, un paquet ne peut plus être modifié, seulement supprimé par le garbage collector. Cette immuabilité garantit qu'aucun processus ne peut corrompre silencieusement une dépendance.

Après quelques installations, le store grandit vite car chaque paquet embarque ses dépendances :

Fenêtre de terminal
du -sh /nix/store/
Résultat
1.2G /nix/store/
Fenêtre de terminal
ls /nix/store/ | wc -l
Résultat
2242

Ne vous inquiétez pas de cette taille : le garbage collector (décrit plus bas) permet de récupérer l'espace des paquets inutilisés.

Une closure est l'ensemble de tous les chemins du store nécessaires pour faire fonctionner un paquet. Quand Nix installe ripgrep, il ne télécharge pas seulement le binaire, il s'assure que toutes ses dépendances (glibc, pcre2, gcc-lib…) sont présentes dans le store.

Fenêtre de terminal
nix path-info -rsSh /nix/store/922crn2k3v8yaqk7anps80hba919lnds-ripgrep-15.1.0
Résultat : closure de ripgrep (54.3 MiB)
/nix/store/p7jg95rzvfalb95k3mskk0jqxc9d724n-libunistring-1.4.1 2.0 MiB 2.0 MiB
/nix/store/1ga782ml07vy0h503ac4cin0h8d7q6yh-libidn2-2.3.8 368.3 KiB 2.3 MiB
/nix/store/hbnbbbx1n96v1waiiaid9fmg4li4i1kp-gcc-15.2.0-libgcc 193.1 KiB 193.1 KiB
/nix/store/jms7zxzm7w1whczwny5m3gkgdjghmi2r-glibc-2.42-51 33.5 MiB 36.0 MiB
/nix/store/ab3753m6i7isgvzphlar0a8xb84gl96i-gcc-15.2.0-lib 9.8 MiB 46.0 MiB
/nix/store/ygdgqjzw03k8d05zq3pigzf67b62j7vs-pcre2-10.46 2.0 MiB 38.0 MiB
/nix/store/922crn2k3v8yaqk7anps80hba919lnds-ripgrep-15.1.0 6.4 MiB 54.3 MiB

La colonne de gauche indique la taille propre du paquet, celle de droite sa taille de closure (le paquet + toutes ses dépendances transitives). Ici, ripgrep pèse 6.4 MiB mais sa closure complète atteint 54.3 MiB à cause principalement de la glibc.

Un paquet présent dans le store n'est pas pour autant exécutable depuis votre shell : rien n'a été ajouté à votre PATH. C'est le rôle du profil, un répertoire d'agrégation qui rassemble par liens symboliques les binaires des paquets que vous avez explicitement demandés. Cette indirection est ce qui rend possible le rollback : changer de version revient à repointer un seul lien, sans rien copier ni supprimer.

Installer un paquet ne copie rien dans /usr/bin. Nix crée une chaîne de liens symboliques qui connecte votre $PATH au store :

~/.nix-profile → ~/.local/state/nix/profiles/profile
└→ profile-2-link → /nix/store/nr80...-profile/
└→ bin/
├── jq → /nix/store/fc13...-jq-1.8.1-bin/bin/jq
└── rg → /nix/store/922c...-ripgrep-15.1.0/bin/rg

Vérifions dans le lab :

Fenêtre de terminal
ls -la ~/.nix-profile
Résultat
~/.nix-profile -> /home/lab/.local/state/nix/profiles/profile
Fenêtre de terminal
ls -la ~/.local/state/nix/profiles/
Résultat
profile -> profile-2-link
profile-1-link -> /nix/store/ka87x2xwr14ry1c1nzcf5ii5lqmhfpla-profile
profile-2-link -> /nix/store/nr80jc6l6jp00c9s3imrvrfx258njr0p-profile
Fenêtre de terminal
ls ~/.nix-profile/bin/
Résultat
jq rg

Chaque profile-N-link pointe vers un répertoire du store qui agrège tous les paquets de cette génération. Quand vous ajoutez un paquet, Nix crée un nouveau répertoire d'agrégation dans le store et fait pointer le lien profile vers celui-ci.

Nix gère deux niveaux de profils, et ils n'ont ni le même propriétaire ni les mêmes conséquences en cas d'erreur. Le profil utilisateur vit dans votre HOME et ne concerne que vous ; le profil système contient l'installation de Nix elle-même. Retenez la ligne du bas, c'est celle qui casse une machine quand on la nettoie sans réfléchir :

ProfilCheminUsage
Utilisateur~/.local/state/nix/profiles/profilePaquets installés par l'utilisateur courant
Système/nix/var/nix/profiles/defaultPaquets installés pour tout le système (inclut nix lui-même)

Nix ne modifie jamais un profil en place : il en construit une nouvelle version et déplace un lien. Chacune de ces versions est une génération, numérotée et conservée tant que vous ne la supprimez pas explicitement. Revenir en arrière après une mise à jour ratée ne demande donc ni sauvegarde ni réinstallation, et prend le temps d'un changement de lien symbolique.

À chaque ajout ou suppression de paquet, Nix crée une nouvelle génération du profil. L'ancienne génération reste intacte dans le store, rien n'est écrasé.

Installons jq et observons l'historique :

Fenêtre de terminal
nix profile add nixpkgs#jq
Fenêtre de terminal
nix profile history
Résultat
Version 1 (2026-04-14):
flake:nixpkgs#legacyPackages.x86_64-linux.ripgrep: ∅ -> 15.1.0
Version 2 (2026-04-14) <- 1:
flake:nixpkgs#legacyPackages.x86_64-linux.jq: ∅ -> 1.8.1-bin, 1.8.1-man

La Version 1 contient uniquement ripgrep. La Version 2 ajoute jq et référence la Version 1 comme parent (<- 1).

Pour revenir à la génération précédente :

Fenêtre de terminal
nix profile rollback
Résultat
switching profile from version 2 to 1

Après rollback, seul rg est disponible dans le profil :

Fenêtre de terminal
ls ~/.nix-profile/bin/
Résultat
rg

jq a disparu du profil, mais il est toujours présent dans le store. Nix n'a fait que changer le lien symbolique profile pour pointer vers profile-1-link au lieu de profile-2-link.

Pour revenir à la génération 2 :

Fenêtre de terminal
nix profile rollback --to 2
Résultat
switching profile from version 1 to 2
Fenêtre de terminal
ls ~/.nix-profile/bin/
Résultat
jq rg

La sortie ne montre que les paquets explicitement installés, pas leurs dépendances, et détaille les chemins du store associés (ici jq en fournit deux, un pour le binaire, un pour les pages man).

Fenêtre de terminal
nix profile list
Résultat
Name: jq
Flake attribute: legacyPackages.x86_64-linux.jq
Store paths: /nix/store/crwv17pim6csnfr4jmsd7kf60sp3c5dh-jq-1.8.1-man
/nix/store/fc13hvlj7541i1xmwdka7f61qicdzr5a-jq-1.8.1-bin
Name: ripgrep
Flake attribute: legacyPackages.x86_64-linux.ripgrep
Store paths: /nix/store/922crn2k3v8yaqk7anps80hba919lnds-ripgrep-15.1.0

Libérer de l'espace : garbage collection et optimisation

Section intitulée « Libérer de l'espace : garbage collection et optimisation »

Le store ne se vide jamais tout seul, il n'a aucune purge automatique. Le nettoyage se fait en deux temps, et l'ordre compte : d'abord supprimer les anciennes générations, ce qui retire les références qui protégeaient les paquets, puis lancer le garbage collector, qui efface ce qui n'est plus référencé. Une passe de déduplication vient ensuite grignoter ce qui reste.

Le store accumule les paquets au fil du temps : anciennes générations, paquets testés via nix shell, dépendances de build intermédiaires. Tant qu'une racine GC (GC root) référence un chemin, il est protégé de la suppression.

Les racines GC principales sont :

  • les générations de profil (profile-1-link, profile-2-link…),
  • les résultats de nix build liés dans ./result,
  • les profils système.

Avant de lancer le garbage collector, supprimez les générations obsolètes pour les retirer des racines GC :

Fenêtre de terminal
nix profile wipe-history --older-than 1d

Cette commande supprime toutes les générations de plus d'un jour. Seule la génération active est conservée.

La commande parcourt le store et supprime tout chemin qu'aucune racine GC ne référence. Elle affiche chaque suppression puis un bilan chiffré, elle est irréversible : un paquet effacé sera retéléchargé ou recompilé s'il redevient nécessaire.

Fenêtre de terminal
nix store gc
Résultat (extrait)
deleting '/nix/store/i4q069m3d29gi7dw1ijhjrp4hxw1c2l4-module-dir.patch'
deleting '/nix/store/d77svhhwwqkvacx0gdyscjp4jdfjkh9d-gcc-15.patch'
...
deleting unused links...
533 store paths deleted, 242.6 MiB freed

Le GC a récupéré 242 MiB en supprimant 533 chemins qui n'étaient plus référencés par aucune racine.

Fenêtre de terminal
du -sh /nix/store/
Résultat
660M /nix/store/

Le store est passé de 1.2 Go à 660 Mo.

Même après le GC, certains fichiers sont dupliqués entre paquets (bibliothèques partagées, fichiers de configuration identiques). La commande optimise les remplace par des hard links :

Fenêtre de terminal
nix store optimise
Résultat
15.2 MiB freed by hard-linking 1230 files
Fenêtre de terminal
du -sh /nix/store/
Résultat
647M /nix/store/

Voici les quatre commandes à retenir pour une maintenance périodique. Les deux premières s'enchaînent dans cet ordre ; la dernière, --max, sert quand vous voulez récupérer une quantité donnée sans vider tout ce qui est récupérable.

CommandeEffet
nix profile wipe-history --older-than 7dSupprime les générations de plus de 7 jours
nix store gcSupprime les chemins non référencés
nix store optimiseDéduplique les fichiers identiques par hard links
nix store gc --max 1GLibère au maximum 1 Go

Le schéma remet les trois couches dans l'ordre où votre shell les traverse. Il n'y a qu'un seul lien modifiable dans toute la chaîne, celui qui désigne la génération active ; tout ce qui se trouve en dessous est immuable. C'est cette asymétrie qui explique à la fois l'atomicité des installations et la gratuité du rollback.

┌─────────────────────────────────────────────────┐
│ Votre shell │
│ $PATH contient ~/.nix-profile/bin/ │
└────────────────────┬────────────────────────────┘
│ symlink
┌────────────────────▼────────────────────────────┐
│ Profil utilisateur │
│ ~/.local/state/nix/profiles/profile │
│ → profile-2-link (génération active) │
│ → profile-1-link (ancienne, rollback possible) │
└────────────────────┬────────────────────────────┘
│ symlink vers store
┌────────────────────▼────────────────────────────┐
│ /nix/store/ │
│ nr80...-profile/bin/ → agrège les paquets │
│ 922c...-ripgrep-15.1.0/bin/rg │
│ fc13...-jq-1.8.1-bin/bin/jq │
│ jms7...-glibc-2.42-51/lib/ │
│ (immuable, identifié par hash) │
└──────────────────────────────────────────────────┘

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

6 questions
6 min.
70% requis

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

  • Le store (/nix/store/) contient tous les paquets, chacun identifié par un hash unique, deux versions cohabitent sans conflit.
  • Une closure est l'ensemble complet des dépendances d'un paquet ; c'est l'unité de déploiement de Nix.
  • Les profils exposent les binaires via des chaînes de liens symboliques, sans toucher à /usr/bin.
  • Chaque modification du profil crée une nouvelle génération ; le rollback est instantané et atomique.
  • Le garbage collector ne supprime que les chemins sans racine GC, vos paquets actifs sont toujours protégés.
  • nix store optimise déduplique les fichiers identiques par hard links pour réduire l'empreinte disque.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn