Aller au contenu
Sécurité medium

NixOS : c'est quoi, pour qui, et comment l'essayer

56 min de lecture

Logo NixOS

NixOS est une distribution Linux dont la particularité tient en une phrase : le système entier se déclare dans des fichiers. Le bootloader, les services, les utilisateurs, les paquets, tout est décrit, et une commande construit le système correspondant. Chaque construction produit une génération conservée à côté des précédentes, ce qui rend le retour arrière atomique et rapide. Cette page définit NixOS sans le vendre, montre ce que le rollback répare et ce qu'il ne répare pas, expose les pièges serveur mesurés en laboratoire, et se termine par un lab d'installation en VM KVM pour vous faire votre propre avis.

Tout ce qui suit a été rejoué sur une machine réelle en NixOS 26.05 « Yarara » (release nixos-26.05.4659.8f0500b96605, noyau 6.18.38, Nix 2.34.7, bootloader systemd-boot). Les sorties de commandes affichées sont celles de cette machine, pas des reconstitutions.

  • Définir NixOS en termes concrets : système déclaré, store, générations, rollback atomique
  • Distinguer ce que NixOS garantit vraiment de ce que le marketing lui prête (la reproductibilité bit-à-bit n'est pas la promesse)
  • Mesurer les limites du rollback : un seul cran, pas les fichiers de conf, pas les données
  • Identifier les pièges serveur : secrets en clair, RAM, /boot, absence de LTS, perte d'accès SSH
  • Décider si NixOS a sa place chez vous, ou si c'est un mauvais investissement dans votre contexte
  • Installer NixOS 26.05 dans une VM KVM avec flakes et disko, et jouer un premier rollback

NixOS en une page : ce que c'est, ce que ça change

Section intitulée « NixOS en une page : ce que c'est, ce que ça change »

Sur une distribution classique, l'état du système est le résultat d'une histoire : les paquets installés, les fichiers édités, les services activés, dans l'ordre où on l'a fait. Personne ne connaît cette histoire en entier, et deux serveurs censés être identiques finissent par diverger. C'est la dérive de configuration.

NixOS renverse le raisonnement. Vous ne décrivez pas des actions, vous décrivez un état cible. Le fichier configuration.nix dit ce que la machine doit être, nixos-rebuild se charge de l'y amener. Ajouter un serveur web se résume à une ligne.

services.nginx.enable = true;

Un nixos-rebuild switch plus tard, le paquet est installé, la configuration générée, l'unité systemd créée, activée et démarrée. Aucun apt install, aucun systemctl enable, aucun fichier édité à la main. Retirez la ligne, rebuildez : le service, son utilisateur système et sa configuration disparaissent.

Chaque paquet et chaque configuration système vivent dans /nix/store/, sous un chemin préfixé par un hash calculé à partir de toutes les entrées de la construction (code source, dépendances, options de compilation). Deux versions d'un même paquet ont deux chemins et cohabitent sans conflit : il n'y a pas d'écrasement, donc pas d'installation « par-dessus ». Ce répertoire est monté en lecture seule, et c'est la garantie structurelle de tout le modèle.

Fenêtre de terminal
mount | grep -E "on / | /nix/store"
# /dev/vda2 on / type ext4 (rw,relatime,x-initrd.mount)
# /dev/vda2 on /nix/store type ext4 (ro,nosuid,nodev,relatime)

Le mécanisme du store, des profils et des générations est détaillé dans le guide Nix store, profils et générations. Retenez ici la conséquence, qui est le point le plus mal compris de NixOS : ce qui n'est pas dans le store n'est pas versionné, donc n'est pas rollbackable.

Les générations : un point de restauration par construction

Section intitulée « Les générations : un point de restauration par construction »

Chaque nixos-rebuild switch produit une génération : un lien vers une closure complète du store, conservé à côté des précédentes. Le système actif est celui vers lequel pointe /run/current-system.

Fenêtre de terminal
nixos-rebuild list-generations
# Generation Build-date NixOS version Kernel Current
# 2 2026-07-12 22:19:29 26.05.4659.8f0500b96605 6.18.38 True
# 1 2026-07-12 22:04:49 25.11.12484.b6018f87da91 6.12.93 False

Chaque génération dépose aussi son noyau et son initrd dans /boot, plus une entrée pour le bootloader. C'est ce qui permet de redémarrer sur un système d'il y a trois semaines, avec son propre noyau. C'est aussi ce qui fait grossir /boot, et le piège associé est traité plus bas.

Le bénéfice réel : configuration reproductible, pas reproductibilité bit-à-bit

Section intitulée « Le bénéfice réel : configuration reproductible, pas reproductibilité bit-à-bit »

C'est la nuance qui sépare un guide honnête d'une plaquette commerciale. Les hashes de Nix portent sur les entrées de la construction (le graphe de dépendances), pas sur les octets produits en sortie : un paquet dont la compilation embarque un horodatage donnera deux binaires différents pour un même hash d'entrée. Dire que « NixOS garantit des builds reproductibles bit à bit » est faux, et l'exagération est régulièrement démontée en public.

Ce que NixOS garantit vraiment, et qui suffit largement à justifier son existence, tient en deux points : la même configuration redonne le même système déployé, et le retour en arrière est atomique. Vérifié en rejouant le lab deux fois de bout en bout : à configuration identique, les chemins du store obtenus sont les mêmes, au caractère près.

NixOS n'est pas un système « immuable » au sens de Fedora Silverblue ou Talos, où l'on remplace une image système signée. Ici, l'immuabilité vient du store en lecture seule et de la régénération complète de /etc à chaque activation, pas d'une image scellée : la page Systèmes d'exploitation immuables situe les deux approches. Ce n'est pas non plus un gestionnaire de paquets que l'on poserait sur Debian : ça, c'est Nix, traité dans la section dédiée. NixOS est ce qui arrive quand on pousse Nix jusqu'au système d'exploitation entier.

Voici l'affirmation qu'on lit partout : sur NixOS, une configuration invalide est attrapée à la construction, donc elle ne peut pas casser la production. C'est faux, et le lab le prouve en une manipulation. Il faut distinguer deux classes d'erreurs, et une seule des deux est réellement interceptée.

Classe 1 : l'erreur d'évaluation, attrapée avant que rien ne bouge

Section intitulée « Classe 1 : l'erreur d'évaluation, attrapée avant que rien ne bouge »

Vous vous trompez dans le nom d'une option. L'évaluation échoue immédiatement, avant toute construction et avant toute activation. Aucune génération n'est créée, le système ne bouge pas d'un pouce, et NixOS va jusqu'à suggérer la correction.

error: The option `services.nginx.enabel' does not exist. Definition values:
- In `/etc/nixos/configuration.nix': true
Did you mean `services.nginx.enable', `services.nginx.gitweb' or `services.nginx.user'?

C'est le filet dont tout le monde parle, et il est réel. Une faute de frappe dans un nom d'option ne peut pas atteindre la production.

Classe 2 : l'erreur de contenu, qui passe le build et casse l'activation

Section intitulée « Classe 2 : l'erreur de contenu, qui passe le build et casse l'activation »

Maintenant, injectez une directive invalide dans une chaîne libre passée à un service. Ici, services.nginx.appendHttpConfig reçoit une directive nginx qui n'existe pas. Nix ne connaît pas la grammaire de nginx : pour lui, c'est du texte.

Le résultat est sans appel : le build réussit, la génération est créée, et c'est l'activation qui explose.

restarting the following units: nginx.service
Failed to restart nginx.service
Command 'systemd-run [...] switch-to-configuration switch' returned non-zero exit status 4.

Le journal montre que c'est le pré-démarrage de l'unité qui refuse la configuration, une fois le système déjà basculé.

nginx-pre-start[6693]: nginx: [emerg] unknown directive "directive_qui_nexiste_pas" in /nix/store/zl8...-nginx.conf:28
systemd[1]: Failed to start Nginx Web Server.

État résultant, mesuré : systemctl is-active nginx répond activating, curl http://localhost renvoie une connexion refusée, le site est down, et nixos-rebuild list-generations affiche bien la nouvelle génération. Le système a basculé sur une configuration qui ne fonctionne pas.

Un détail gênant mérite d'être signalé tel quel : l'option services.nginx.validateConfigFile vaut true par défaut (vérifié avec nixos-option sur la machine), et la configuration invalide a quand même été construite. Un nginx -t lancé à la main sur le fichier produit dans le store la rejette pourtant sans ambiguïté. Ce lab n'a pas élucidé pourquoi la validation au build n'a pas joué, et il serait malhonnête d'inventer une explication.

Ce que ce contre-exemple doit changer dans votre pratique

Section intitulée « Ce que ce contre-exemple doit changer dans votre pratique »

La leçon n'est pas « NixOS est dangereux ». La leçon est que le vrai filet de sécurité n'est pas le build, c'est le rollback. Toute configuration qui passe une chaîne libre à un service (nginx, PostgreSQL, sshd, systemd) échappe à la validation de Nix, parce que Nix ne parle pas ces langages. Deux réflexes en découlent :

  • nixos-rebuild test avant switch sur les changements sensibles : la configuration est activée mais ne devient pas le défaut au démarrage. Un simple reboot ramène le système connu.
  • Savoir rollbacker de tête, sans chercher la commande. Elle prend 2 secondes, c'est l'opération la plus rapide de tout NixOS.

Le rollback : rapide, spectaculaire, et plus limité qu'on ne le dit

Section intitulée « Le rollback : rapide, spectaculaire, et plus limité qu'on ne le dit »

Le rollback est le geste emblématique de NixOS, et il tient ses promesses. Une commande, deux secondes, et le système redevient exactement ce qu'il était. Voici la sortie réelle après avoir ajouté puis retiré nginx.

Fenêtre de terminal
sudo nixos-rebuild switch --rollback
# switching profile from version 2 to 1
# stopping the following units: [...] nginx.service
# removing user 'nginx'

Vérification immédiate : systemctl status nginx répond Unit nginx.service could not be found. et curl échoue avec Failed to connect to localhost port 80. Le service a réellement disparu, avec son utilisateur système. Rien n'a été « désinstallé » : le système a simplement réactivé une closure du store qui n'a jamais cessé d'exister.

Ce mécanisme est excellent. Il a quatre limites, et toutes les quatre coûtent cher le jour où on les découvre en production.

Limite 1 : --rollback ne recule que d'un seul cran

Section intitulée « Limite 1 : --rollback ne recule que d'un seul cran »

Beaucoup de billets écrivent « revenez à n'importe quelle génération avec --rollback ». C'est faux. L'option recule d'exactement une génération, celle qui précède l'actuelle. Deux appels successifs donnent, dans le lab : switching profile from version 11 to 10, puis from version 10 to 9. Pour viser une génération arbitraire, il faut passer par le lien du profil correspondant, ce que détaille le guide dédié.

Limite 2 : le rollback annule le système, pas le fichier

Section intitulée « Limite 2 : le rollback annule le système, pas le fichier »

Après un --rollback, votre configuration.nix contient toujours la ligne fautive. Le système est sain, le fichier ne l'est pas : les deux ont divergé. Le prochain nixos-rebuild switch que fera un collègue, pour une raison sans rapport, remettra la panne. Le rollback est une opération sur le profil système, pas sur votre dépôt.

Limite 3 : le garbage collector détruit le rollback, et laisse un menu menteur

Section intitulée « Limite 3 : le garbage collector détruit le rollback, et laisse un menu menteur »

nix-collect-garbage sans option libère de la place sans toucher aux générations (410 Mio récupérés dans notre lab). Avec -d, il supprime les anciennes racines, et le filet disparaît.

Fenêtre de terminal
nixos-rebuild switch --rollback
# error: no profile version older than the current (11) exists

Le piège dans le piège est plus vicieux. Juste après le -d, /boot/loader/entries/ contient toujours les 11 fichiers, dont ceux des générations qui viennent d'être détruites. Le menu de démarrage propose donc des entrées qui ne démarreront pas : le chemin du store qu'elles référencent n'existe plus. C'est le rebuild suivant qui nettoie l'ESP. Entre les deux, la machine expose un menu mensonger, et c'est précisément le moment où vous en auriez besoin.

Limite 4 : le rollback ne restaure pas les données

Section intitulée « Limite 4 : le rollback ne restaure pas les données »

C'est la limite la plus importante, et la moins écrite. Nous l'avons mise en scène avec PostgreSQL et une vraie base.

Une table commandes contient trois lignes. L'application fait une bêtise et les efface. Réflexe NixOS : je rollback, ça répare tout.

Fenêtre de terminal
sudo -u postgres psql -d boutique -c "DELETE FROM commandes;"
# DELETE 3
sudo nixos-rebuild switch --rollback
# switching profile from version 26 to 25

Le système est bien revenu en arrière : le chemin de /run/current-system est identique à celui d'avant. Et les lignes ?

Fenêtre de terminal
sudo -u postgres psql -d boutique -c "SELECT * FROM commandes;"
# id | client | montant
# ----+--------+---------
# (0 rows)

Le système est revenu en arrière, les lignes non. Le rollback ne sauvegarde rien : il réactive une ancienne closure du store, point final. Il ne défait même pas un DELETE.

La preuve est structurelle, pas anecdotique. Un grep récursif du contenu applicatif dans /nix/store ne remonte aucune donnée : elles vivent dans /var/lib/postgresql/, hors du store, sur un système de fichiers en lecture-écriture, et le store est de toute façon monté en lecture seule. Ce qui n'est pas dans le store n'est pas versionné, donc n'est pas rollbackable. Un rollback n'est pas une sauvegarde : il ne remplace ni pg_dump, ni un snapshot ZFS, ni votre plan de reprise.

Le cas de la montée de version majeure est encore plus parlant. Passer de PostgreSQL 16.14 à 17.10 se fait en changeant une ligne de la configuration.

services.postgresql = {
enable = true;
package = pkgs.postgresql_17; # au lieu de postgresql_16
};

Le rebuild passe en 10 secondes, le service redémarre, SELECT version() annonce fièrement PostgreSQL 17.10. Sauf que le répertoire de données a changé de /var/lib/postgresql/16 à /var/lib/postgresql/17, que ce nouveau répertoire est vide, que PostgreSQL l'a donc initialisé de zéro, et que la base de l'application n'existe plus.

Fenêtre de terminal
sudo -u postgres psql -d boutique -c "SELECT * FROM commandes;"
# psql: error: [...] FATAL: database "boutique" does not exist

Aucune erreur, aucun avertissement : un service actif, une base neuve, et vos données toujours sur le disque mais plus servies par personne. Le rollback vers 16 les retrouve intactes, ce qui est heureux, mais imaginez la scène si le garbage collector était passé entre-temps, ou si l'application avait écrit dans la base neuve. C'est exactement pourquoi l'option system.stateVersion existe, et pourquoi le manuel écrit que la plupart des utilisateurs ne devraient jamais y toucher : elle fige le choix de version des logiciels qui ne savent pas migrer leurs données tout seuls. Ce n'est pas un numéro de version à incrémenter, contrairement à ce que conseillent beaucoup de billets.

Le rollback, la sélection d'une génération dans systemd-boot, le mode rescue et le garbage collection sont traités scénario par scénario dans le guide dédié.

La question qui vient toujours : et si je mets ma conf dans Git ?

Section intitulée « La question qui vient toujours : et si je mets ma conf dans Git ? »

C'est le réflexe de tout le monde, et c'est un bon réflexe. Versionner /etc/nixos dans un dépôt Git donne l'historique, la revue, le git revert, et l'impression rassurante d'avoir tout sous contrôle. Le lab a joué le scénario en entier, et la réponse est plus nuancée que « oui ».

Git règle le problème du fichier, celui de la limite 2 ci-dessus. Après un rollback système, un git revert du commit fautif remet le dépôt d'accord avec le système : les deux cessent de diverger, et le nixos-rebuild switch d'un collègue ne réintroduit pas la panne. Git permet aussi de remonter loin, là où --rollback ne recule que d'un cran : cinq commits en arrière se retrouvent avec un git checkout.

En revanche, Git ne restaure pas les données. Votre dépôt décrit le système, pas le contenu de /var/lib. Toutes les conclusions de la section précédente restent valables telles quelles.

Le piège des canaux : la révision de nixpkgs n'est pas dans votre dépôt

Section intitulée « Le piège des canaux : la révision de nixpkgs n'est pas dans votre dépôt »

Le scénario monté par le lab est déterminant. Une machine installée avec les canaux (le mécanisme historique, encore celui du manuel). Le dépôt est propre, on note le chemin du système au commit A. Le temps passe : le canal nixos-26.05 avance tout seul, un nix-channel --update récupère une nouvelle révision de nixpkgs. On ajoute un paquet, c'est le commit B.

Panne. On revient au commit A avec Git. Le fichier est exactement celui du commit A, diff le confirme. On rebuilde. Le système obtenu est-il celui du commit A ?

SA (rebuild du commit A à l'époque, canal 4494) : /nix/store/4v0bm1hz...-nixos-system-nixos-lab-26.05.4494.74cc63f702f7
SA' (rebuild du commit A aujourd'hui, canal 4659) : /nix/store/6drz9k5y...-nixos-system-nixos-lab-26.05.4659.8f0500b96605
==> DIFFERENTS : git seul NE SUFFIT PAS, la revision de nixpkgs vient du canal, pas du depot

Deux systèmes différents, à partir du même commit. Les paquets ne sont plus les mêmes, le noyau non plus. Où vit la révision qui a changé ? Nulle part dans votre dépôt : dans le profil de canal, sous /nix/var/nix/profiles/per-user/root/channels/. Le verdict brut du lab est sans appel : aucun fichier du dépôt ne contient la révision de nixpkgs, c'est là qu'est la faille.

Une flake est un format de projet Nix qui déclare ses dépendances (les inputs) et verrouille leurs révisions exactes dans un fichier flake.lock, comme un package-lock.json. La différence capitale avec les canaux : ce flake.lock est un fichier du dépôt, donc versionné avec le reste.

Le même scénario, rejoué sur une machine installée par flake : commit A avec un lock sur une révision, nix flake update qui fait avancer le lock, commit B, puis retour au commit A avec Git.

SA (commit A à l'époque) : /nix/store/njqz7kjd...-nixos-system-nixos-lab2-26.05.20260708.74cc63f
SA' (commit A rebâti après update) : /nix/store/njqz7kjd...-nixos-system-nixos-lab2-26.05.20260708.74cc63f
==> IDENTIQUES : Git + flake.lock redonnent EXACTEMENT le meme systeme

Le même chemin du store, au hash près. La conclusion opérationnelle est simple et vaut d'être retenue : Git seul ne suffit pas, il faut Git plus un verrou de version. Sur une machine de production, c'est l'argument le plus solide en faveur des flakes, bien avant les considérations d'élégance.

Une réserve honnête : les flakes sont toujours officiellement expérimentales en 2026, alors même que 78,9 % des répondants à l'enquête communautaire 2025 les utilisent. Ce décalage a une conséquence concrète en entreprise : le mot « expérimental » bloque des validations sécurité, indépendamment de la qualité réelle du mécanisme. Il faut le savoir avant de bâtir une flotte dessus. Le mécanisme lui-même est expliqué dans Comprendre et utiliser les flakes Nix.

Les douleurs de NixOS sur un poste de travail (pilotes NVIDIA, Steam, AppImage, Python scientifique) sont abondamment documentées, et elles disparaissent sur un serveur. Mais le serveur a ses propres pièges, moins racontés parce qu'ils se révèlent tard. Voici ceux que le lab a reproduits.

Un secret écrit dans un .nix finit en clair, lisible par tous

Section intitulée « Un secret écrit dans un .nix finit en clair, lisible par tous »

C'est le piège le plus grave, parce qu'il est silencieux. /nix/store est lisible par tous les utilisateurs de la machine, par construction. Tout secret écrit dans un fichier .nix y atterrit en clair.

/nix/store/wkjdh7cm...-users-groups.json.drv
# Depuis un compte utilisateur ordinaire, sans aucun privilège
grep -rl MonMotDePasse123 /nix/store/ | head -2
# /nix/store/66g24jlv...-users-groups.json

Un utilisateur non privilégié relit le mot de passe. NixOS n'a pas de solution native à ce problème. La parade la plus simple est agenix, qui chiffre les secrets avec la clé SSH de l'hôte et ne les déchiffre qu'à l'activation. Vérifié sur la machine : après chiffrement, le grep dans le store ne trouve plus rien, et ce qui s'y trouve est un fichier .age chiffré. Le secret déchiffré, lui, atterrit dans /run/agenix/ avec des permissions -r-------- root:root (un compte ordinaire obtient Permission denied). Le point de montage mérite qu'on s'y arrête.

Fenêtre de terminal
findmnt -no FSTYPE -T /run/agenix
# ramfs

Un ramfs, pas un tmpfs. La différence n'est pas cosmétique : un tmpfs peut être envoyé dans le swap, donc écrit sur disque, alors qu'un ramfs ne swappe jamais. Le secret déchiffré ne touche jamais le stockage persistant, et c'est exactement le genre de détail qui fait la différence dans une revue de sécurité.

Dernier avertissement : le secret qui a fuité avant la mise en place d'agenix est toujours dans le store, référencé par une ancienne génération, tant que celle-ci n'a pas été collectée. Un secret ayant fuité dans le store est à considérer comme compromis, donc à faire tourner.

La RAM : l'évaluation coûte cher, et la marge est nulle sur un petit VPS

Section intitulée « La RAM : l'évaluation coûte cher, et la marge est nulle sur un petit VPS »

Chiffre rarement publié, et pourtant décisif pour qui déploie sur des VPS. Une simple réévaluation de la configuration, tout étant déjà en cache, sans rien à télécharger ni à compiler, consomme :

User time (seconds): 3.12
Elapsed (wall clock) time: 0:04.02
Maximum resident set size (kbytes): 606400

606 Mo de mémoire résidente juste pour évaluer nixpkgs. Ce n'est pas un build : c'est le simple fait de calculer ce que le système devrait être. Avec une configuration plus fournie (nginx plus PostgreSQL), le pic monte à 632 Mo.

Nous avons délibérément joué la scène sur une VM à 1 Go (957 Mo réels, 706 Mo disponibles, aucun swap). Le verdict est plus nuancé que ce qu'on lit : le rebuild passe. Aucun OOM dans dmesg, le système reste debout, les services démarrent. Mais faites le calcul : 606 Mo consommés sur 706 disponibles, la marge est de 100 Mo. Ajoutez un second canal, une configuration plus riche ou un service qui mange déjà sa part, et vous basculez. Un administrateur ayant documenté le cas sur un VPS 1 Go mesure 500 Mo en configuration minimale, 700 Mo avec des services, 1,2 Go avec un second canal, et rapporte un OOM franc lors d'une montée de version.

La formulation juste est donc : un rebuild tient sur 1 Go avec une configuration minimale, mais la marge est nulle. Ce n'est pas une plateforme sur laquelle on veut être quand la production est en jeu.

La parade est structurelle, et c'est l'une des bonnes idées de NixOS : construisez ailleurs, déployez la closure. L'outil officiel nixos-rebuild le fait nativement avec --target-host : le poste d'administration évalue et construit, la cible se contente de recevoir des chemins du store et d'activer. Le lab l'a poussé jusqu'à l'absurde en supprimant tout canal nixpkgs de la cible : elle devient alors incapable d'évaluer quoi que ce soit, et le déploiement fonctionne quand même. La preuve est faite qu'elle n'a rien construit.

Chaque génération pousse son noyau et son initrd dans la partition EFI. Sur notre montée de version, /boot est passé de 25 Mo à 64 Mo avec seulement deux générations, et le store de 2,0 Go à 4,2 Go. Multipliez par vingt générations, sur une ESP de 512 Mo dimensionnée par un installateur d'origine, et vous saturez.

Le problème n'est pas l'espace disque : c'est que le rebuild peut échouer en cours d'écriture dans /boot et laisser la machine avec zéro entrée de boot valide. Sur un serveur distant sans console, cela signifie un déplacement physique. L'issue nixpkgs#141399, « Unbootable system when /boot partition fills up during rebuild », est toujours ouverte. Le garde-fou existe, il n'est simplement pas activé par défaut : déclarez-le dès la première configuration d'un serveur, c'est une ligne et elle vous évitera une nuit blanche.

boot.loader.systemd-boot.configurationLimit = 10;

Pas de LTS : sept mois de support, contre cinq ans chez Debian

Section intitulée « Pas de LTS : sept mois de support, contre cinq ans chez Debian »

C'est le fait le plus structurant pour qui envisage NixOS en production, et il n'a rien de technique. NixOS sort deux versions par an (mai et novembre), chacune supportée environ sept mois. 26.05 « Yarara », sortie le 30 mai 2026, est maintenue jusqu'au 31 décembre 2026. 25.11 « Xantusia » est morte le 30 juin 2026 : 26.05 est aujourd'hui la seule version stable maintenue. Il n'existe aucune LTS, et un fil communautaire évoquant une LTS payante confirme en creux qu'il n'y en a pas de gratuite.

NixOSDebian
Rythme de publication2 versions par anenviron tous les 2 ans
Support d'une versionenviron 7 mois5 ans (avec le support long terme)
Montées de version imposées2 par an1 tous les 2 ans, planifiable

Concrètement : deux montées de version majeures par an, non négociables. Si votre organisation peine déjà à passer une Debian tous les deux ans, NixOS sera un problème d'exploitation, quelles que soient ses qualités techniques. La comparaison avec un cycle long se mesure sur la page Debian 13 Trixie : ce qui change pour un serveur.

Pas de rollback automatique si vous perdez l'accès SSH

Section intitulée « Pas de rollback automatique si vous perdez l'accès SSH »

Le rollback exige un accès à la machine. Déployez une configuration qui casse sshd, le pare-feu ou le réseau, et vous perdez cet accès : le filet est hors de portée, précisément quand vous en auriez besoin. Ce n'est pas un oubli mineur, l'issue nixpkgs#65477, « Request for fail-safe rollback », est ouverte depuis le 27 juillet 2019 et l'est toujours. nixos-rebuild n'offre aucun filet de ce type. La parade tient en une commande, et elle est gratuite.

Fenêtre de terminal
sudo nixos-rebuild test --flake .#nixos-lab

test active la nouvelle configuration mais ne la rend pas bootable par défaut. Si vous perdez la main, un simple redémarrage (bouton reset de l'hyperviseur, console cloud) ramène le système connu. Le lab a vérifié ce comportement avec deux redémarrages réels : après un test, le service ajouté est actif immédiatement, aucune génération ni entrée de boot n'est créée, et après reboot il a disparu. C'est le mode à utiliser sur tout changement qui touche l'accès à la machine.

Le binaire précompilé ne s'exécute pas, et le message a changé

Section intitulée « Le binaire précompilé ne s'exécute pas, et le message a changé »

Vous téléchargez un agent de supervision, un EDR, un client de sauvegarde, un binaire fourni par un éditeur. Sur Debian, il tourne. Sur NixOS, non. La cause est que le chargeur dynamique standard (/lib64/ld-linux-x86-64.so.2) n'existe pas ici, et les binaires génériques le cherchent à un chemin en dur.

Attention aux vieux billets : le fameux cannot execute: required file not found n'est plus le message en 26.05. NixOS installe désormais un stub-ld qui explique le problème au lieu de le subir.

$ /root/ls-ubuntu /tmp
Could not start dynamically linked executable: /root/ls-ubuntu
NixOS cannot run dynamically linked executables intended for generic
linux environments out of the box. For more information, see:
https://nix.dev/permalink/stub-ld

Diagnostic : /lib64/ld-linux-x86-64.so.2 existe bien, mais c'est un leurre qui pointe vers ce stub, et /lib n'existe pas du tout.

La parade dominante est le module officiel nix-ld, qui recrée un vrai chargeur. Mais ne le vendez pas comme une case à cocher : programs.nix-ld.enable = true; ne suffit pas. Le chargeur est alors trouvé, et le binaire échoue aussitôt sur la bibliothèque suivante.

$ /root/ls-ubuntu /tmp
/root/ls-ubuntu: error while loading shared libraries: libselinux.so.1: cannot open shared object file

Il faut déclarer explicitement les bibliothèques dont le binaire a besoin. Pour un simple /bin/ls d'Ubuntu, le jeu complet ressemble à ceci, et il a fallu deux itérations pour le trouver :

programs.nix-ld.enable = true;
programs.nix-ld.libraries = with pkgs; [
libselinux pcre2 acl attr libcap zlib stdenv.cc.cc.lib
];

L'angle serveur est celui-ci : ce sont les agents (supervision, EDR, sauvegarde, agent d'un fournisseur) qui coincent, parce qu'ils supposent une arborescence Debian ou RedHat. Avant de choisir NixOS pour une flotte, faites l'inventaire des binaires propriétaires que vous devez y faire tourner. C'est souvent là que la décision se joue.

Le dépaysement : /usr, /etc, et un nginx qui ne sert aucune page

Section intitulée « Le dépaysement : /usr, /etc, et un nginx qui ne sert aucune page »

Trois surprises attendent tout administrateur venant de Debian, et il vaut mieux les rencontrer dans un lab que sur un serveur.

D'abord, l'arborescence Unix n'existe presque plus. Il n'y a aucun gestionnaire de paquets classique, et les répertoires historiques sont vides : ls /usr renvoie bin, ls /usr/bin renvoie env, ls /bin renvoie sh. Deux rescapés, et rien d'autre, tous deux des liens vers le store. Ils survivent parce que trop de scripts au monde en dépendent.

Ensuite, /etc est régénéré à chaque activation. Le hotfix à trois heures du matin n'existe pas.

Fenêtre de terminal
echo "hotfix-manuel" > /etc/hostname
# bash: /etc/hostname: Read-only file system

Même en supprimant le lien pour écrire un vrai fichier, le nixos-rebuild switch suivant efface le hotfix et remet son lien vers /etc/static/. Il n'y a pas de correction rapide : il y a une modification de la configuration, un rebuild, et une trace dans Git. Selon votre culture d'équipe, c'est un soulagement ou une torture.

Enfin, la surprise la plus déroutante : services.nginx.enable = true; démarre bien le service (systemctl is-active nginx répond active), et pourtant rien n'écoute sur le port 80. curl échoue, ss -lntp ne montre aucun port en écoute : aucun hôte virtuel n'est déclaré par défaut, alors que sur Debian un apt install nginx sert immédiatement sa page d'accueil. Accessoirement, /etc/nginx/nginx.conf n'existe pas : la configuration vit dans le store, référencée par l'unité systemd. Chercher le fichier de configuration d'un service est un réflexe à désapprendre.

Monter de version : 25.11 vers 26.05, ce que ça donne

Section intitulée « Monter de version : 25.11 vers 26.05, ce que ça donne »

Puisque 25.11 est morte et que la migration est obligatoire, autant la voir en vrai. Le lab a installé une VM en 25.11 « Xantusia » (noyau 6.12.93, Nix 2.31.5) et l'a fait monter par la procédure officielle : on change le canal, puis on reconstruit.

Fenêtre de terminal
sudo nix-channel --add https://channels.nixos.org/nixos-26.05 nixos
sudo nix-channel --update
sudo nixos-rebuild switch --upgrade
nixos-version
# 26.05.4659.8f0500b96605 (Yarara)
nixos-rebuild list-generations
# 2 2026-07-12 22:19:29 26.05.4659.8f0500b96605 6.18.38 True
# 1 2026-07-12 22:04:49 25.11.12484.b6018f87da91 6.12.93 False

La montée fonctionne, l'ancienne génération reste disponible, et un rollback ramène la 25.11 en une seconde et demie. Trois observations méritent d'être notées.

Le noyau ne change qu'au redémarrage : juste après le switch, nixos-version annonce 26.05 mais uname -r répond toujours 6.12.93. Après reboot, 6.18.38. C'est la raison d'être des générations dans le bootloader.

Le store double de volume, de 2,0 Go à 4,2 Go, parce que les deux versions cohabitent tant que le garbage collector n'est pas passé. Prévoyez la place sur une racine étriquée.

Enfin, system.stateVersion reste à 25.11, et c'est normal. Beaucoup de billets conseillent de le « bumper » : ne le faites pas. Ce n'est pas un numéro de version, c'est le marqueur qui dit avec quelle génération de logiciels vos données existantes ont été créées. Le modifier peut rendre vos bases illisibles. Dernière précision, qui vous évitera de chercher : en 26.05, nixos-rebuild est une réécriture complète en Python (nixos-rebuild-ng), l'implémentation Bash ayant été supprimée, et quelques options ont bougé (--sudo remplace --use-remote-sudo). Les tutoriels antérieurs à 2026 décrivent l'ancienne.

Pour qui c'est un bon choix, pour qui c'est un piège

Section intitulée « Pour qui c'est un bon choix, pour qui c'est un piège »

Le moment de trancher. Ni promotion, ni dénigrement : NixOS est un excellent outil dans un périmètre précis, et un mauvais investissement en dehors.

SituationVerdict
Une flotte de serveurs à configuration homogène, gérée en équipe, avec revue de codeBon choix. La configuration déclarative et le rollback atomique rendent la dérive impossible.
Environnements de développement et CI reproductiblesBon choix, et c'est l'usage le plus répandu en entreprise (souvent avec Nix seul, sans NixOS).
Un poste d'administration ou un bastion à durcir et auditerBon choix. Tout est déclaré, donc tout est auditable. Le retour d'expérience Sécurix le démontre en conditions réelles.
Une seule machine, gérée par une personneProbablement pas. La complexité ne se rentabilise pas sur un seul système.
Des agents propriétaires obligatoires (EDR, supervision, sauvegarde d'un éditeur)Terrain miné. Chaque binaire non packagé est un chantier nix-ld ou une dérivation à écrire.
Une organisation qui ne sait pas monter de version tous les six moisPiège. L'absence de LTS n'est pas contournable.
Une base de données critique, sans stratégie de sauvegarde proprePiège dangereux. Le rollback donne une illusion de filet qu'il n'offre pas sur les données.

La réserve la plus utile ne vient pas d'un détracteur, mais d'un utilisateur convaincu. Michael Stapelberg, développeur Debian et ancien ingénieur Google, a migré son NAS vers NixOS en juillet 2025. Il apprécie de gérer tout le système déclarativement et juge les modules NixOS d'un niveau d'abstraction supérieur aux conteneurs. Sa conclusion mérite d'être citée telle quelle :

if you have just a single system to manage, probably all of this is too complicated.

NixOS est un investissement dont le retour vient du nombre de machines et de la durée. Sur une seule machine, vous payez la complexité sans jamais toucher le dividende.

Un mot sur la maturité, pour finir. Le projet a traversé une crise de gouvernance en 2024 (démission du créateur du board, départs de mainteneurs, fork Lix), mais une nouvelle gouvernance a été élue en novembre 2024 : il n'est pas acéphale. Les critiques persistantes portent ailleurs, et l'enquête communautaire 2025 les chiffre : 47,6 % des utilisateurs réclament de meilleurs messages d'erreur, 41,5 % un meilleur manuel. Quand la moitié des convaincus demande ça, c'est structurel, et c'est ce qui rend la courbe d'apprentissage réellement raide.

La meilleure façon de se faire un avis reste de poser le système sur une machine jetable et de le casser. Ce lab installe NixOS 26.05 dans une VM KVM, avec un partitionnement déclaratif via disko et une configuration pilotée par une flake. Comptez trente minutes, et vous pourrez tout détruire sans rien risquer.

Avant de commencer, vérifiez que vous disposez de :

  • Un poste Linux avec KVM/QEMU et libvirt (virsh, virt-install)
  • Le firmware OVMF pour le boot UEFI (/usr/share/OVMF/OVMF_CODE_4M.fd sur Debian et Ubuntu)
  • 8 Go de RAM à allouer à la VM d'installation
  • 20 Go d'espace disque pour l'image QCOW2
  • L'ISO NixOS 26.05 minimal (environ 1,6 Go)
  • Une clé SSH existante sur l'hôte

Le point sur la RAM n'est pas une précaution de confort. Avec 4 Go, notre installation a échoué : l'ISO tient son store de travail dans un tmpfs dimensionné sur la mémoire vive, et l'évaluation de nixpkgs le sature.

error: write of 470 bytes: No space left on device
error: could not find a flake.nix file

Le message est trompeur (il parle d'espace disque alors que le problème est la RAM) et fait perdre beaucoup de temps. Allouez 8 Go pour l'installation. La VM installée, elle, tourne sans problème avec 2 Go.

Le lab tient en trois fichiers, plus le verrou généré. Comprendre leur rôle respectif est plus important que de les recopier.

FichierRôle
flake.nixPoint d'entrée : déclare les dépendances (nixpkgs, disko) et la configuration NixOS
disko-config.nixSchéma de partitionnement déclaratif du disque
configuration.nixLe système : bootloader, réseau, utilisateurs, paquets, services
flake.lockLes révisions verrouillées des dépendances (généré, mais à versionner)

Le flake déclare deux choses : les inputs (ses dépendances, avec leurs versions) et les outputs (ce qu'il produit, ici une configuration nommée nixos-lab).

flake.nix
{
description = "NixOS Lab - VM KVM de test";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, disko }: {
nixosConfigurations.nixos-lab = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
./disko-config.nix
./configuration.nix
];
};
};
}

La ligne inputs.nixpkgs.follows = "nixpkgs" force disko à utiliser la même version de nixpkgs que votre configuration. Sans elle, deux arborescences nixpkgs cohabitent en mémoire et l'évaluation coûte deux fois plus cher.

Disko décrit le partitionnement de manière déclarative, ce qui évite le fdisk manuel et le rend rejouable.

disko-config.nix
{ lib, ... }:
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/vda";
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

Le device = "/dev/vda" correspond au disque virtio dans QEMU. Sur une machine physique, adaptez (/dev/nvme0n1, /dev/sda) et vérifiez deux fois : disko formate sans poser de question.

Vient enfin le cœur du système, où chaque ligne correspond à quelque chose de concret sur la machine.

configuration.nix
{ config, pkgs, modulesPath, ... }:
{
# Profil QEMU : charge les modules virtio dans l'initrd
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
# Bootloader UEFI
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Garde-fou contre la saturation de /boot
boot.loader.systemd-boot.configurationLimit = 10;
# Modules noyau pour la virtualisation
boot.initrd.availableKernelModules =
[ "virtio_pci" "virtio_blk" "virtio_scsi" "ahci" "sd_mod" ];
boot.kernelModules = [ "virtio_net" ];
networking.hostName = "nixos-lab";
time.timeZone = "Europe/Paris";
i18n.defaultLocale = "fr_FR.UTF-8";
console.keyMap = "fr";
# SSH par clé uniquement
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = false;
};
};
users.users.lab = {
isNormalUser = true;
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAA... votre-cle-publique"
];
};
# Sudo sans mot de passe : VM de test UNIQUEMENT
security.sudo.wheelNeedsPassword = false;
environment.systemPackages = with pkgs; [
vim git htop curl tmux
];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Garbage collection automatique
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
system.stateVersion = "26.05";
}

Quatre lignes méritent une explication, parce qu'elles font la différence entre un système qui démarre et un système qui ne démarre pas :

  • imports = [ qemu-guest.nix ] charge les modules virtio dans l'initrd. Sans elle, le noyau ne trouve pas le disque et vous restez bloqué sur waiting for device.
  • configurationLimit = 10 empêche /boot de se remplir indéfiniment. Ce n'est pas le défaut.
  • security.sudo.wheelNeedsPassword = false est acceptable sur une VM jetable et inacceptable ailleurs. Ne recopiez pas cette ligne sur un serveur.
  • system.stateVersion est la version de NixOS à l'installation initiale. Elle protège vos données : ne la modifiez jamais après coup, même en montant de version.

L'anatomie de configuration.nix, l'exploration des options avec nixos-option, la factorisation en modules et la gestion des utilisateurs sont traitées dans le guide dédié.

Récupérez l'ISO minimale de NixOS 26.05, puis créez le disque et la VM.

Fenêtre de terminal
mkdir -p ~/lab-nixos/images && cd ~/lab-nixos/images
wget https://channels.nixos.org/nixos-26.05/latest-nixos-minimal-x86_64-linux.iso
qemu-img create -f qcow2 ~/lab-nixos/images/nixos-lab.qcow2 20G

Le format QCOW2 est sparse : le fichier ne consomme que l'espace réellement utilisé. Le store d'un système fraîchement installé pèse 2,2 Go mesurés.

Fenêtre de terminal
virt-install \
--name nixos-lab \
--ram 8192 \
--vcpus 4 \
--disk path=$HOME/lab-nixos/images/nixos-lab.qcow2,format=qcow2,bus=virtio \
--cdrom $HOME/lab-nixos/images/latest-nixos-minimal-x86_64-linux.iso \
--os-variant nixos-unstable \
--network network=default,model=virtio \
--graphics vnc,listen=127.0.0.1 \
--boot loader=/usr/share/OVMF/OVMF_CODE_4M.fd,loader.readonly=yes,loader.type=pflash,loader.secure=no \
--noautoconsole

Vérification : virsh list --all doit montrer la VM en état running.

L'ISO démarre sur un shell non privilégié : le compte est nixos, pas root. C'est la première chose qui surprend, et tout tutoriel qui enchaîne des commandes de partitionnement sans sudo échoue avec un Permission denied. Le compte nixos est dans le groupe wheel avec sudo sans mot de passe.

SSH, lui, refuse les connexions tant qu'aucun mot de passe n'est défini. Il faut en poser un temporairement via la console.

  1. Définir un mot de passe sur l'installateur depuis l'hôte, en pilotant la console.

    Fenêtre de terminal
    # Attendre ~40 secondes que l'ISO démarre
    virsh send-key nixos-lab KEY_P KEY_A KEY_S KEY_S KEY_W KEY_D KEY_ENTER
    sleep 2
    for key in KEY_N KEY_I KEY_X KEY_O KEY_S; do
    virsh send-key nixos-lab $key; sleep 0.3
    done
    virsh send-key nixos-lab KEY_ENTER
    sleep 1
    for key in KEY_N KEY_I KEY_X KEY_O KEY_S; do
    virsh send-key nixos-lab $key; sleep 0.3
    done
    virsh send-key nixos-lab KEY_ENTER

    Le délai de 0,3 seconde entre les touches n'est pas décoratif : plus court (0,15 s), la console virtuelle perd des lettres et votre mot de passe n'est pas celui que vous croyez.

  2. Se connecter à l'installateur après avoir récupéré l'adresse attribuée par DHCP.

    Fenêtre de terminal
    virsh domifaddr nixos-lab
    sshpass -p 'nixos' ssh -o StrictHostKeyChecking=no nixos@192.168.122.x
  3. Copier les trois fichiers de configuration dans la VM, puis les placer dans un répertoire de travail.

    Fenêtre de terminal
    # Depuis l'hôte
    sshpass -p 'nixos' scp -o StrictHostKeyChecking=no \
    ~/lab-nixos/nixos-config/*.nix nixos@192.168.122.x:/tmp/nixos-config/

    N'oubliez pas de remplacer "ssh-ed25519 AAAA... votre-cle-publique" dans configuration.nix par le contenu réel de votre ~/.ssh/id_ed25519.pub, sinon vous ne pourrez pas vous connecter au système installé.

  4. Verrouiller les dépendances en générant le flake.lock.

    Fenêtre de terminal
    # Dans la VM
    cd /tmp/nixos-config
    sudo nix flake lock

    Cette commande télécharge les index de nixpkgs et disko et fige leurs révisions exactes. C'est ce fichier, et lui seul, qui rend votre installation rejouable à l'identique dans six mois.

  5. Partitionner et formater avec disko, qui prend en charge le partitionnement, le formatage et le montage en une seule commande.

    Fenêtre de terminal
    sudo nix run github:nix-community/disko -- \
    --mode disko /tmp/nixos-config/disko-config.nix
    lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT /dev/vda
    # NAME FSTYPE SIZE MOUNTPOINT
    # vda 20G
    # ├─vda1 vfat 512M /mnt/boot
    # └─vda2 ext4 19,5G /mnt

    Les partitions montées sous /mnt signalent que tout est prêt.

  6. Installer le système.

    Fenêtre de terminal
    sudo mkdir -p /mnt/etc/nixos
    sudo cp -r /tmp/nixos-config/* /mnt/etc/nixos/
    sudo nixos-install --flake /mnt/etc/nixos#nixos-lab --no-root-passwd

    L'installation télécharge le complément de la closure depuis le cache binaire, construit le système et installe le bootloader. Le message installation finished! confirme le succès. Comptez une à deux minutes : l'essentiel de la closure est déjà sur l'ISO. --no-root-passwd est légitime ici, puisque la configuration n'autorise que l'accès SSH par clé.

  7. Redémarrer sur le disque en éjectant l'ISO.

    Fenêtre de terminal
    virsh change-media nixos-lab sda --eject
    virsh reboot nixos-lab

    Si la VM affiche un shell UEFI au lieu de démarrer, l'ordre de boot place encore le CD-ROM en premier : virsh destroy nixos-lab, retirez l'entrée <boot dev="cdrom"/> avec virsh edit, puis virsh start nixos-lab.

Connectez-vous par clé SSH, sans mot de passe, et validez le système. Voici les sorties réelles de la machine du lab.

Fenêtre de terminal
ssh lab@192.168.122.x
nixos-version
# 26.05.4659.8f0500b96605 (Yarara)
uname -r && hostname
# 6.18.38
# nixos-lab
lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT /dev/vda
# vda 20G
# ├─vda1 vfat 512M /boot
# └─vda2 ext4 19,5G /
du -sh /nix/store
# 2.2G /nix/store
readlink -f /run/current-system
# /nix/store/84137f6mhq4ygh3snwsz0xazw9z5qhhz-nixos-system-nixos-lab-26.05.4659.8f0500b96605
grep -E "^(PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config
# PasswordAuthentication no
# PermitRootLogin prohibit-password

Le lien /run/current-system est le plus instructif : c'est la génération active, un simple pointeur vers une closure du store, et c'est cette commande qui dit quel système tourne réellement. La colonne Current de list-generations désigne, elle, la génération du profil : après un nixos-rebuild boot, elle affiche la nouvelle génération alors que le système en cours d'exécution est encore l'ancien. Ne confondez pas les deux.

Quant au durcissement SSH, vous n'avez édité aucun fichier dans /etc : ces lignes viennent de vos six lignes de configuration.nix, et elles seront régénérées à l'identique à chaque activation. C'est tout le modèle en une démonstration.

Le lab n'a d'intérêt que si vous le cassez. Ajoutez nginx, avec un hôte virtuel (sans lui, le service tourne mais ne sert rien).

services.nginx = {
enable = true;
virtualHosts."localhost" = {
root = "/var/www/html";
locations."/" = {
index = "index.html";
};
};
};
systemd.tmpfiles.rules = [
"d /var/www/html 0755 root root -"
"f /var/www/html/index.html 0644 root root - '<h1>Bonjour depuis NixOS</h1>'"
];

Reconstruisez, puis observez le mécanisme complet.

Fenêtre de terminal
cd /etc/nixos
sudo nixos-rebuild switch --flake .#nixos-lab
curl http://localhost
# <h1>Bonjour depuis NixOS</h1>
nixos-rebuild list-generations
# 1 ... 26.05.4659.8f0500b96605 6.18.38 False
# 2 ... 26.05.4659.8f0500b96605 6.18.38 True

Deux générations coexistent dans le store. Maintenant, annulez.

Fenêtre de terminal
sudo nixos-rebuild switch --rollback
# switching profile from version 2 to 1
# stopping the following units: [...] nginx.service
# removing user 'nginx'
systemctl status nginx.service
# Unit nginx.service could not be found.

Deux secondes. Le service, sa configuration et son utilisateur système ont disparu. Et souvenez-vous du piège de la limite 2 : votre configuration.nix contient toujours le bloc nginx. Le système et le fichier ont divergé, et c'est à vous de les remettre d'accord.

Par honnêteté, et parce qu'un lab qui prétend tout démontrer ne démontre rien : le menu de systemd-boot n'a pas été vu à l'écran (il n'écrit pas sur la console série). Ce qui est prouvé, c'est le contenu de /boot/loader/entries/ (un fichier .conf par génération, distingués par leur ligne version) et le démarrage effectif sur une ancienne génération, obtenu avec bootctl set-oneshot suivi d'un redémarrage. L'affichage graphique du menu, lui, est décrit dans le guide de réparation.

Une variante de ce lab, avec un partitionnement manuel et une flake minimale sans disko, permet de voir exactement ce que disko fait à votre place.

Les erreurs ci-dessous sont celles rencontrées en rejouant ce lab. La colonne « cause » compte plus que la solution : sur NixOS, les messages d'erreur pointent rarement vers le vrai coupable.

SymptômeCause probableSolution
No space left on device alors que le disque est videLa VM n'a que 4 Go de RAM : le tmpfs de l'ISO satureAllouer 8 Go de RAM à la VM d'installation
ISO : « Access Denied » au démarrageSecure Boot activé sur OVMFOVMF_CODE_4M.fd avec loader.secure=no
Permission denied sur l'ISOLe shell de l'ISO est le compte nixos, pas rootPréfixer par sudo (sans mot de passe)
To make it visible to Nix, run: git add ...Un fichier du dépôt Git n'a jamais été indexé : la flake ne le voit pasgit add -A avant tout nixos-rebuild --flake
Boot : « waiting for device disk-main-root »Modules virtio absents de l'initrdAjouter imports = [ qemu-guest.nix ]
Boot : shell UEFI au lieu du systèmeLe CD-ROM est encore premier dans l'ordre de bootÉjecter l'ISO : virsh change-media nixos-lab sda --eject
virsh send-key : des lettres manquentDélai entre touches trop courtPasser à 0,3 seconde par touche
nginx active mais rien sur le port 80Aucun hôte virtuel déclaréDéclarer services.nginx.virtualHosts
Un binaire téléchargé ne s'exécute pasChargeur dynamique absent (stub-ld)programs.nix-ld.enable et programs.nix-ld.libraries
error: no profile version older than the currentUn nix-collect-garbage -d a supprimé les anciennes générationsLe rollback est perdu. Reconstruire depuis Git

Une légende tenace mérite d'être corrigée : on lit partout qu'un dépôt Git est obligatoire pour installer par flake, sous peine d'une erreur d'assertion cryptique. Ce n'est plus vrai en 26.05. Un répertoire nu fonctionne, à condition que flake.lock existe déjà (le premier appel le crée, le second aboutit). Le vrai piège est plus sournois : dans un dépôt Git, tout fichier non indexé par git add est invisible pour la flake. Vous éditez un fichier, il n'a aucun effet, et vous cherchez vingt minutes. Nix a l'élégance de le dire :

To make it visible to Nix, run:
git -C "/etc/nixos" add "flake.nix"

Versionnez tout de même /etc/nixos : ce n'est pas une obligation technique, c'est ce qui verrouille le flake.lock dans l'historique, et c'est la moitié de la démonstration faite plus haut.

  1. NixOS est un système entièrement déclaré : le fichier décrit l'état cible, nixos-rebuild amène la machine à cet état. La dérive de configuration disparaît.
  2. Le bénéfice réel n'est pas la reproductibilité bit-à-bit (les hashes portent sur les entrées), c'est la configuration reproductible plus le rollback atomique.
  3. « Si ça build, ça marche » est faux. Une erreur de nom d'option est attrapée avant tout ; une erreur de contenu passée en chaîne libre à un service passe le build, crée la génération et casse à l'activation. Le vrai filet, c'est le rollback.
  4. Le rollback a quatre limites : un seul cran, il ne corrige pas votre fichier de conf, un nix-collect-garbage -d le détruit en laissant un menu de démarrage mensonger, et il ne restaure pas les données. Un rollback n'est pas une sauvegarde.
  5. Git seul ne suffit pas. Avec les canaux, la révision de nixpkgs n'est pas dans le dépôt : le même commit rebâti plus tard donne un système différent. Avec une flake, le flake.lock est versionné et redonne exactement le même chemin du store.
  6. Aucun secret dans un fichier .nix : /nix/store est lisible par tous. Utilisez agenix ou sops-nix, dont le secret déchiffré atterrit dans un ramfs qui ne part jamais dans le swap.
  7. Prévoyez la RAM et /boot : l'évaluation coûte 606 Mo mesurés en configuration minimale, la marge est nulle sur 1 Go, et boot.loader.systemd-boot.configurationLimit n'est pas activé par défaut.
  8. Pas de LTS, et un investissement qui se rentabilise sur une flotte : deux versions par an et sept mois de support, contre cinq ans chez Debian. Sur une machine unique, vous payez la complexité sans toucher le dividende.

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