Cet article fait suite à la conférence BYOD du 13/10/2026 que j’ai donnée à la Brasserie du Digital.
Tout le monde a déjà prononcé ou entendu cette phrase en réunion technique : « chez moi, ça fonctionne ». Elle précède en général une demi-journée perdue pour un nouvel arrivant, incapable de compiler le projet parce qu’une bibliothèque système manque ou que la version de l’interpréteur diffère de celle du serveur.
Le problème ne vient pas d’un manque de rigueur des équipes. Il découle de la conception même des systèmes d’exploitation classiques.
Un état qu’on ne sait pas décrire précisément ne se rejoue pas.
Tant qu’un système repose sur l’accumulation de modifications manuelles, il reste impossible à reproduire à l’identique d’une machine à l’autre.
Pourquoi un système classique casse
Dans un système Linux traditionnel, l’administration repose sur la mutation. Modifier l’état d’une machine consiste à altérer ses fichiers en place : installer un paquet via le gestionnaire de la distribution, éditer un fichier dans /etc, exécuter un script d’ajustement.
Chaque commande modifie un état existant. Si deux machines partent de la même installation initiale, quelques mois d’exploitation suffisent pour que leurs configurations divergent de façon invisible.
Préparer et activer : la confusion d’origine
Dans une mise à jour ordinaire, deux opérations distinctes sont exécutées en même temps : la préparation du nouvel état et son activation. Le gestionnaire de paquets télécharge des fichiers et écrase immédiatement les binaires et bibliothèques en cours d’utilisation sur le disque.
Si l’opération est interrompue par une coupure réseau, un disque plein ou un script de pré-installation défaillant, le système se retrouve au milieu du gué. Certains fichiers appartiennent à l’ancienne version, d’autres à la nouvelle.
Le retour arrière devient alors un travail d’archéologie : il faut deviner ce qui a été modifié pour tenter de restaurer manuellement l’état antérieur.
Le principe du système immuable
L’approche immuable applique au système d’exploitation ce que les développeurs utilisent quotidiennement avec Git : un état figé, identifié, auquel on peut revenir à tout moment.
La règle fondamentale est simple : on ne modifie jamais un système en place.
Le nouvel état est construit et vérifié intégralement à côté de l’état actif, sans toucher à ce qui tourne. Lorsque tout est prêt, une bascule atomique active la nouvelle version.
En cas d’anomalie au démarrage ou pendant les tests de validation, le retour à l’état précédent prend quelques secondes : il suffit de réactiver l’ancien pointeur.
Trois façons d’aborder l’immuabilité
L’immuabilité s’implémente à différents niveaux d’abstraction, du disque brut jusqu’au paquet logiciel. Trois approches dominent aujourd’hui le paysage technique.
1. Le partitionnement A/B
C’est l’approche retenue par Android depuis la version 7.0, par ChromeOS et par des distributions comme VanillaOS. Le disque comporte deux partitions système identiques, A et B.
Pendant que la partition A fonctionne, le système télécharge et écrit l’image complète de la mise à jour sur la partition B. Au redémarrage suivant, l’amorceur (bootloader) tente de démarrer sur B et exécute des contrôles de santé.
Si les tests réussissent, la partition B devient la référence. Si le démarrage échoue, la machine bascule automatiquement sur la partition A.
Cette mécanique est redoutablement efficace pour des terminaux grand public, mais elle montre vite ses limites en entreprise : elle double l’espace disque requis et impose de fabriquer des images disques monolithiques, peu adaptées à des serveurs sur mesure.
2. L’arbre de fichiers versionné avec OSTree
Créé pour GNOME puis popularisé par Red Hat avec Fedora Silverblue et les distributions de l’écosystème Universal Blue, OSTree applique les principes de Git à l’arborescence des fichiers du système d’exploitation.
Les fichiers système résident dans un dépôt adressé par contenu. Chaque déploiement constitue une révision en lecture seule, et les anciens déploiements restent accessibles au menu de démarrage de la machine. Les applications utilisateur sont quant à elles isolées dans des conteneurs (Flatpak ou Distrobox).
Le revers d’OSTree réside dans sa rigidité. La granularité reste celle d’une image complète fournie par l’éditeur. Pour ajouter un paquet système non prévu, il faut superposer des couches (layering via rpm-ostree), ce qui ralentit la recomposition de l’image et fragilise la reproductibilité de l’ensemble.
3. La dérivation fonctionnelle avec Nix et NixOS
L’approche de Nix, initiée dès 2003 par les travaux de recherche d’Eelco Dolstra, pousse la logique jusqu’au bout. Nix ne raisonne ni en partitions, ni en images : il traite chaque paquet logiciel comme le résultat d’une fonction pure et déterministe.
Une recette Nix prend des entrées explicites (sources figées par une empreinte cryptographique, dépendances, drapeaux de compilation) et produit une dérivation (.drv). L’évaluation de cette dérivation génère un chemin unique et immuable dans le magasin de paquets :
/nix/store/<empreinte>-hello-2.12.1
Puisque l’empreinte dépend rigoureusement de toutes les entrées, deux compilations d’un même logiciel avec des versions différentes de compilateur (par exemple GCC 12 et GCC 13) cohabitent dans /nix/store sans aucun conflit de bibliothèques.
NixOS étend ce principe à l’intégralité du système d’exploitation. Un fichier de configuration, une unité systemd, un compte utilisateur ou le noyau Linux sont tous des dérivations assemblées dans le magasin de paquets.
Le système actif n’est qu’un ensemble de liens symboliques pointant vers ces chemins immuables.
Le magasin immuable et le rôle du démon
Comment garantir qu’un utilisateur ne modifie pas les fichiers du magasin par mégarde ? Le répertoire /nix/store est monté strictement en lecture seule.
Toutes les écritures passent par un service d’arrière-plan unique : le nix-daemon. Lorsqu’un utilisateur lance une commande de construction, sa requête est transmise au démon.
Ce dernier délègue le travail à des comptes système dédiés et isolés (nixbld1, nixbld2…), sans privilèges d’administration et surtout sans accès réseau. Aucun script de construction ne peut télécharger des dépendances cachées en cours de route : toutes les sources doivent avoir été déclarées et vérifiées par leur empreinte cryptographique au préalable.
Une fois la construction achevée avec succès dans un bac à sable hermétique, le résultat est inséré de façon atomique dans le magasin.
Modules nixpkgs : une intention, plusieurs dérivations
Tout écrire soi-même à partir de zéro serait fastidieux. C’est le rôle de nixpkgs, le dépôt communautaire qui contient les recettes prêtes à l’emploi.
On y trouve des paquets individuels, mais surtout des modules. Un module expose des options de haut niveau et génère pour vous l’ensemble des dérivations nécessaires :
services.postgresql = {
enable = true;
port = 5434;
};
Cette simple déclaration produit automatiquement :
- le paquet PostgreSQL avec ses dépendances binaires ;
- les fichiers de configuration associés (
postgresql.conf,pg_hba.conf) ; - l’unité de service systemd ;
- la création de l’utilisateur et du groupe système requis.
L’administrateur déclare l’intention, le module assemble les recettes.
Le plus grand catalogue de paquets
Avec environ 115 000 paquets dans sa branche stable (source Repology, octobre 2026), nixpkgs dépasse largement les distributions classiques comme Debian (~38 000 paquets) ou Ubuntu (~40 000 paquets).
Il intègre environ 23 500 paquets libres, et environ 1 100 paquets sous licence propriétaire (comme certains pilotes matériels ou suites logicielles). Par défaut, Nix refuse d’évaluer ou d’installer un paquet non libre si l’administrateur ne l’a pas explicitement autorisé via config.allowUnfree = true. Si une seule dépendance transitive d’un paquet est non libre, le garde-fou bloque la chaîne.
Le cache binaire officiel
Reconstruire chaque binaire localement paralyserait l’exploitation. Le système s’appuie donc sur un serveur de cache binaire officiel (cache.nixos.org).
Puisqu’une dérivation calcule l’empreinte de son chemin de sortie avant même de compiler, la machine interroge le cache avec cette empreinte : si le binaire précompilé s’y trouve, il est simplement téléchargé. La compilation locale n’intervient que pour du code sur mesure ou des options modifiées.
La rupture avec le standard FHS
Ce modèle a une contrepartie technique majeure qu’il faut assumer : Nix brise délibérément le standard FHS (Filesystem Hierarchy Standard).
Sur NixOS, les dossiers /usr/bin, /bin et /lib n’existent pas sous leur forme habituelle. Seuls /bin/sh et /usr/bin/env sont conservés pour assurer la compatibilité des scripts (qui doivent donc utiliser le shebang #!/usr/bin/env bash plutôt que le classique #!/bin/bash).
Le chemin des binaires utilisables dans le $PATH est assemblé sous forme d’un profil : /run/current-system/sw/bin pointe vers un dossier de liens symboliques dans le magasin. Changer de profil ou basculer de version modifie ce lien de façon atomique.
Un binaire générique téléchargé sur Internet ne fonctionne pas directement : son en-tête ELF recherche un éditeur de liens dynamique /lib64/ld-linux-x86-64.so.2 qui est absent du système.
Pour intégrer ces exécutables précompilés, trois mécanismes pragmatiques existent :
nix-ld: un chargeur dynamique qui intercepte les appels des binaires génériques et les redirige vers les bibliothèques actives du système.patchelf: un utilitaire qui réécrit directement l’en-tête du fichier binaire pour y inscrire le chemin absolu de la glibc et le chemin de recherche des dépendances (RPATH) dans/nix/store.buildFHSEnv: un conteneur léger qui émule une arborescence FHS traditionnelle en mémoire pour exécuter des logiciels tiers non modifiables.
Configuration déclarative d’une machine
Sur NixOS, l’état complet du système est consigné dans un fichier déclaratif. On ne tape plus de commandes pour configurer un service : on décrit le résultat souhaité.
{ config, pkgs, ... }: {
networking.hostName = "serveur-atelier";
time.timeZone = "Europe/Paris";
# Activation d'un service système
services.openssh.enable = true;
# Déclaration d'un compte utilisateur
users.users.alice = {
isNormalUser = true;
extraGroups = [ "wheel" ];
};
# Paquets mis à disposition globale
environment.systemPackages = with pkgs; [
curl
htop
git
];
}
Si une ligne est retirée de ce fichier, le paquet ou le service correspondant disparaît de la machine dès la bascule suivante. Le système reflète exactement ce qui est écrit, sans résidus d’anciennes installations.
Puisque rien n’est jamais écrasé, le magasin de paquets grossit au fil des évolutions. Un ramasse-miettes (garbage collector) automatique se configure en quelques lignes pour purger les générations antérieures à trente jours :
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
L’éléphant dans la pièce : la gestion des données
NixOS garantit la reproductibilité du système et des applications, mais ne versionne pas les données applicatives.
Imaginons le cas concret d’un déploiement le vendredi soir : une application migre sa base de données vers un nouveau schéma ou convertit des documents stockés sur le disque. Si un dysfonctionnement apparaît le samedi, revenir à la version logicielle précédente via NixOS ne prend qu’une commande.
Mais l’ancien binaire sera incapable de lire les données déjà migrées par la nouvelle version.
Revenir en arrière au niveau du système d’exploitation ne sert à rien si les données ont subi une modification irréversible.
La réponse pragmatique consiste à associer NixOS à un système de fichiers avec copie sur écriture (Copy-on-Write), comme Btrfs ou ZFS. Juste avant la bascule du système, un instantané (snapshot) des sous-volumes de données est déclenché.
En cas d’incident, on restaure simultanément la génération NixOS et l’instantané Btrfs : le code et les données retrouvent rigoureusement leur état d’origine.
Flakes : figer l’ensemble au commit près
Historiquement, deux machines évaluant une même configuration Nix pouvaient diverger si leurs canaux de paquets n’étaient pas synchronisés au même instant.
Le mécanisme des Flakes, standardisé depuis 2020, résout cette limite. Un fichier flake.nix explicite les entrées (inputs) et les sorties (outputs), tandis qu’un fichier flake.lock verrouille les sommes de contrôle au commit près (comme le font package-lock.json ou Cargo.lock).
Ce fichier unique permet également de gérer un parc hétérogène de machines à partir d’un seul dépôt Git :
# Construire la configuration d'un portable sans l'activer
nixos-rebuild build --flake .#portable
# Déployer et activer la configuration du serveur d'atelier à distance
nixos-rebuild switch --flake .#atelier --target-host root@atelier
L’outillage de l’écosystème
Plusieurs outils viennent se greffer naturellement autour des Flakes pour l’exploitation en production :
home-manager: applique la même logique déclarative à l’environnement utilisateur (outils en ligne de commande, fichiers de configuration de session).deploy-rs: outil de déploiement à distance sans agent lourd. En cas de dysfonctionnement (par exemple si une règle de pare-feu bloque la connexion SSH après mise à jour),deploy-rsdétecte l’échec et déclenche seul un retour arrière automatique vers la configuration fonctionnelle précédente.sops-nix: gestion des secrets chiffrés directement dans le dépôt Git (via l’outil SOPS et des clés PGP ou age), déchiffrés en mémoire vive au démarrage de la machine sans laisser de trace sur le disque.llm-agents.nix: distribution reproductible d’agents et d’outils d’intelligence artificielle en ligne de commande.
Docker et NixOS : complémentarité et passerelles
Une question revient fréquemment chez les équipes techniques : Nix remplace-t-il Docker ?
La réponse est non. Docker et NixOS répondent à des problématiques différentes et fonctionnent très bien ensemble :
- NixOS pilote les conteneurs : l’option
virtualisation.oci-containerspermet de déclarer des conteneurs Docker ou Podman directement dans le fichier de configuration de l’hôte. Chaque conteneur devient une unité systemd versionnée et intégrée à la machine. compose2nix: cet outil convertit automatiquement un fichierdocker-compose.ymlexistant en configuration NixOS native, évitant toute réécriture manuelle lors d’une phase de transition.devenv container: Nix sait construire directement des images de conteneurs de production minimalistes, sans démon Docker et sans empiler des couches intermédiaires inutiles.- L’image Docker officielle : la fondation NixOS fournit une image
docker.io/nixos/nixqui permet d’utiliser le gestionnaire de paquets au sein de pipelines d’intégration continue classiques.
Par où commencer : l’adoption progressive avec devenv.sh
Passer un parc de serveurs ou des postes de travail sous NixOS du jour au lendemain est rarement réaliste. En revanche, l’immuabilité peut s’adopter dès demain sur un projet précis, sans changer de système d’exploitation, sur Linux comme sur macOS.
L’outil devenv.sh s’appuie sur Nix pour créer des environnements de développement isolés, décrits dans un simple fichier devenv.nix :
{ config, pkgs, ... }:
let
dbName = "mon_projet";
in {
packages = with pkgs; [ gcc git openssl ];
# Variables d'environnement par défaut
dotenv.enable = true;
languages.javascript = {
enable = true;
nodejs.enable = true;
npm = {
enable = true;
install = true;
};
};
# Service de test local Mailpit
services.mailpit.enable = true;
# Base PostgreSQL locale avec extension vectorielle
services.postgres = {
enable = true;
port = 5433;
extensions = extensions: [
extensions.pgvector
];
initialDatabases = [{ name = dbName; }];
};
}
Les données des services locaux (comme PostgreSQL) sont stockées dans le répertoire local .devenv, évitant d’encombrer le système global ou de risquer des conflits de ports avec d’autres projets.
L’exécution repose sur deux commandes simples :
devenv shell(ou automatiquement dès lecdgrâce àdirenv) pour entrer dans l’environnement avec les compilateurs et dépendances verrouillés ;devenv uppour lancer les services locaux d’arrière-plan.
Le fichier devenv.lock assure que chaque développeur de l’équipe partage rigoureusement les mêmes binaires, sans jamais entendre à nouveau « ça marche chez moi ».
De la mutation à l’immuabilité
L’approche repose sur trois piliers indissociables :
- Le système déclaratif : la configuration devient du code source versionné.
- L’immuabilité : la bascule entre versions est atomique, sans écrasement en place.
- La reproductibilité : les dépendances et empreintes sont verrouillées au commit près.
Ce n’est pas un changement d’outil, mais un changement de posture : cesser de subir l’état imprévisible d’un système pour en reprendre la maîtrise complète.
Questions fréquentes
Que se passe-t-il si un paquet n’existe pas dans nixpkgs ?
Il est possible d’écrire sa propre dérivation en quelques lignes dans le dépôt du projet. Nix permet également d’intégrer directement un dépôt Git distant comme entrée via les Flakes, ou d’encapsuler un binaire externe via patchelf ou buildFHSEnv.
La courbe d’apprentissage n’est-elle pas trop abrupte ?
Oui, pour un système complet. Le langage Nix et la disparition du standard FHS imposent un changement d’habitudes significatif. C’est pourquoi commencer par devenv.sh sur un projet applicatif permet d’en récolter les bénéfices immédiats (reproductibilité des postes, zéro conflit de versions) sans subir la complexité de gestion du système hôte.
Pourquoi préférer NixOS à des scripts Ansible ?
Ansible applique des mutations successives sur une machine existante. Si une étape échoue ou qu’une modification manuelle a eu lieu entre-temps, le résultat final n’est plus garanti. NixOS ne modifie rien sur place : il calcule un état complet et bascule atomiquement, garantissant que deux exécutions d’une même configuration produisent exactement la même machine.
À retenir
- La mutation traditionnelle traite la préparation et l’activation en même temps, rendant les mises à jour fragiles et les retours arrière incertains.
- L’immuabilité prépare le nouvel état intégralement à côté de l’existant, puis bascule de manière atomique.
- Nix et NixOS transforment la configuration et les paquets en fonctions pures, garantissant une reproductibilité au bit près.
- L’adoption la plus pragmatique commence au niveau du projet de développement grâce à
devenv.sh, avant d’envisager le système d’exploitation.
C’PRAG accompagne les entreprises dans la fiabilisation et la reprise en main de leurs infrastructures logicielles et systèmes.
L’approche pragmatique
- Diagnostic matériel de la reproductibilité des environnements.
- Cartographie des dépendances invisibles et points de divergence.
- Isolation des briques d’exécution et mise sous contrôle des versions.
- Mise en place de bascules atomiques et procédures de retour arrière vérifiées.
En une à deux journées, sans interrompre la production.
Réservez un échange préalable de 15 minutes pour en parler : un environnement de développement qui met une demi-journée à s’installer est le premier signe d’un système qui a perdu sa reproductibilité.
Sources
- Corentin VÉROT VASSAL. Conférence BYOD Systèmes immuables : fiabilité et reproductibilité, La Brasserie du Digital (Le Puy-en-Velay, 13 octobre 2026). Télécharger le support (PDF).
- Dolstra, Eelco. The Purely Functional Software Deployment Model (PhD Thesis, 2006).
- Repology - Statistiques des dépôts de paquets (octobre 2026).
- Documentation officielle NixOS et nixpkgs.
- Projet devenv.sh - Fast, Declarative, Reproducible Developer Environments.