Aller au contenu
Administration Linux medium

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

12 min de lecture

Le store Nix (/nix/store/) est le cœur du système : chaque paquet y vit dans un répertoire unique 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.

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.

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 :

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)

À 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
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 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.

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/
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
┌─────────────────────────────────────────────────┐
│ 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) │
└──────────────────────────────────────────────────┘
  • 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 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