Introduction
Combien de fois avez-vous entendu — ou prononcé — la phrase « ça marche sur ma machine » ? Ce problème fondamental du développement logiciel persiste depuis des décennies malgré les conteneurs, les machines virtuelles et les outils de gestion de configuration. La raison est simple : la plupart de ces outils gèrent les changements d'état de manière impérative, sans garantir la reproductibilité.
Nix prend une approche radicalement différente. Inspiré des concepts de programmation fonctionnelle, Nix est un gestionnaire de paquets purement fonctionnel où chaque paquet est défini par ses entrées (code source, dépendances, options de compilation) et produit une sortie déterministe. Si les entrées sont identiques, la sortie est identique — sur n'importe quelle machine, à n'importe quel moment.
Architecture en un coup d'œil

NixOS pousse cette philosophie encore plus loin : c'est un système d'exploitation Linux entier configuré de manière déclarative. Votre système — services, utilisateurs, réseau, pare-feu, tout — est décrit dans un fichier de configuration. Un changement dans ce fichier produit une nouvelle « génération » du système, avec la possibilité de rollback atomique vers n'importe quelle génération précédente.
En 2025-2026, Nix connaît une croissance explosive. Nixpkgs, son dépôt de paquets, est devenu le plus grand dépôt de paquets au monde avec plus de 100 000 paquets — dépassant l'AUR d'Arch Linux. Les flakes, le nouveau système de composition, ont révolutionné la reproductibilité des environnements de développement. Et des projets comme Home Manager, devenv.sh et Cachix rendent l'écosystème accessible à un public de plus en plus large.
Dans cet article, nous allons explorer en profondeur Nix et NixOS : leur fonctionnement, leur installation, leurs cas d'usage, et comment ils peuvent transformer votre workflow de développement et de déploiement.
Qu'est-ce que Nix ? Le gestionnaire de paquets fonctionnel
Le problème que Nix résout
Les gestionnaires de paquets traditionnels (apt, dnf, pacman, brew) partagent un modèle commun : ils installent les paquets dans des emplacements globaux partagés (/usr/lib, /usr/bin, etc.) et modifient l'état du système de manière impérative et mutable. Ce modèle pose plusieurs problèmes :
- Conflits de dépendances (dependency hell) : le projet A nécessite OpenSSL 1.1, le projet B nécessite OpenSSL 3.0. Les deux ne peuvent pas coexister dans
/usr/lib - Non-reproductibilité : installer les mêmes paquets sur deux machines peut produire des résultats différents selon l'ordre d'installation, les mises à jour intermédiaires, etc.
- Mises à jour destructives : une mise à jour peut casser des logiciels qui dépendaient de la version précédente, sans moyen simple de revenir en arrière
- Impossible de tester avant de committer : on ne peut pas facilement tester un changement d'état du système sans l'appliquer
- Pas de rollback fiable :
apt removene restaure pas l'état précédent du système
Le modèle fonctionnel de Nix
Nix résout ces problèmes en empruntant les concepts de la programmation fonctionnelle pure :
- Pureté : le résultat d'une « build » ne dépend que de ses entrées explicitement déclarées. Pas d'effets de bord, pas de dépendances implicites sur l'état du système
- Immutabilité : une fois construit, un paquet ne change jamais. Il n'est pas « mis à jour » — une nouvelle version est un nouveau paquet distinct
- Adressage par le contenu (content-addressable) : chaque paquet est stocké dans un chemin unique dérivé d'un hash cryptographique de toutes ses entrées
- Composition : les systèmes complexes sont construits en composant des paquets simples, de manière déterministe
Le Nix Store : /nix/store
Le cœur de Nix est le Nix Store, un répertoire (/nix/store) où chaque paquet est stocké dans un chemin unique basé sur un hash de ses entrées :
# Exemples de chemins dans le Nix Store
/nix/store/w0bgiyx6r9ahqjhpwn7gyp5jbqxs24nl-python3-3.12.7/
/nix/store/a4kif8z5rs3h2grwkzipbx0g12m21dql-python3-3.12.8/
/nix/store/k3bc4bpg1wn0f7n3jb2g7q3l1yvz6g5h-openssl-3.3.2/
/nix/store/m6v2dsh7rp9nz5k0h8j4x1q2w3e4r5t6-nodejs-22.11.0/
/nix/store/p7w3e4r5t6y7u8i9o0a1s2d3f4g5h6j7-nginx-1.27.3/
# Le hash (les 32 premiers caractères) est calculé à partir de :
# - Le code source du paquet
# - Toutes les dépendances (récursivement)
# - Les options de compilation
# - Le compilateur utilisé
# - Les patches appliqués
# Cela signifie que deux versions différentes de Python coexistent
# sans aucun conflit :
ls /nix/store/ | grep python3-3.12
# w0bgiyx6r9ahqjhpwn7gyp5jbqxs24nl-python3-3.12.7
# a4kif8z5rs3h2grwkzipbx0g12m21dql-python3-3.12.8
# Chaque paquet contient sa propre arborescence complète
ls /nix/store/w0bgiyx6r9ahqjhpwn7gyp5jbqxs24nl-python3-3.12.7/
# bin/ include/ lib/ share/
# Les dépendances sont des liens vers d'autres chemins dans le store
ldd /nix/store/w0bgiyx6r9ahqjhpwn7gyp5jbqxs24nl-python3-3.12.7/bin/python3
# /nix/store/k3bc4bpg1wn0f7n3jb2g7q3l1yvz6g5h-openssl-3.3.2/lib/libssl.so.3
# /nix/store/...-glibc-2.40/lib/libc.so.6Ce modèle permet :
- Coexistence de versions : plusieurs versions d'un même paquet coexistent sans conflit
- Reproductibilité : le même hash = le même paquet, garanti
- Rollback atomique : revenir à une version précédente = pointer vers l'ancien chemin dans le store
- Garbage collection : les paquets non référencés peuvent être supprimés avec
nix-collect-garbage - Partage : si deux utilisateurs ont besoin du même paquet, il n'est stocké qu'une seule fois
Nixpkgs : le plus grand dépôt de paquets au monde
Nixpkgs est le dépôt central de paquets Nix, hébergé sur GitHub (github.com/NixOS/nixpkgs). En 2025, il contient plus de 100 000 paquets, ce qui en fait le plus grand dépôt de paquets de toute distribution Linux. À titre de comparaison :
| Dépôt | Nombre de paquets (approx. 2025) |
|---|---|
| Nixpkgs | 100 000+ |
| AUR (Arch Linux) | ~85 000 |
| Debian (stable) | ~60 000 |
| Fedora | ~55 000 |
| Homebrew | ~7 000 |
Nixpkgs est maintenu par une communauté de plus de 6 000 contributeurs et suit un modèle de branches :
- nixpkgs-unstable : les dernières versions de tous les paquets (rolling release)
- nixos-24.11 : branche stable, mise à jour pendant ~7 mois après la release
- nixos-25.05 : la prochaine release stable (mai 2025)
Installer Nix
Nix peut être installé sur n'importe quelle distribution Linux et sur macOS. Il ne remplace pas votre gestionnaire de paquets existant — il coexiste avec lui. Vous pouvez utiliser Nix aux côtés d'apt, dnf, pacman ou brew sans aucun conflit.
Installation recommandée : le Determinate Nix Installer
# Installation recommandée avec le Determinate Nix Installer
# (installation multi-user avec flakes activés par défaut)
curl --proto '=https' --tlsv1.2 -sSf -L \
https://install.determinate.systems/nix | sh -s -- install
# Alternative : installateur officiel NixOS
sh <(curl -L https://nixos.org/nix/install) --daemon
# Vérifier l'installation
nix --version
# nix (Nix) 2.25.3
# Tester avec un paquet
nix run nixpkgs#hello
# Hello, world!
# Lancer un shell avec un paquet temporaire (sans installation permanente)
nix shell nixpkgs#python3 nixpkgs#nodejs
python3 --version # Python 3.12.x
node --version # v22.x.x
exit # Les paquets disparaissent
# Chercher un paquet
nix search nixpkgs firefox
nix search nixpkgs "python3.*packages"Installation sur macOS
# Sur macOS, Nix fonctionne parfaitement
curl --proto '=https' --tlsv1.2 -sSf -L \
https://install.determinate.systems/nix | sh -s -- install
# Nix crée un volume APFS dédié pour /nix/store sur macOS
# (nécessaire car le rootfs macOS est en lecture seule)
df -h /nix
# /dev/disk1s7 460Gi 2.1Gi 458Gi 1% /nix
# Utiliser des paquets Linux sur macOS
nix shell nixpkgs#ripgrep nixpkgs#fd nixpkgs#bat nixpkgs#jq
rg --version # Compilé nativement pour macOS/ARM64nix-shell : environnements de développement reproductibles
nix-shell est l'un des outils les plus puissants de Nix. Il permet de créer des environnements de développement éphémères et reproductibles contenant exactement les outils dont vous avez besoin, sans polluer votre système :
# Shell temporaire avec Python, Node.js et PostgreSQL client
nix-shell -p python3 nodejs postgresql
# Dans ce shell :
python3 --version # 3.12.x
node --version # 22.x.x
psql --version # 16.x
exit # Tout disparaît
# Shell avec des paquets Python spécifiques
nix-shell -p 'python3.withPackages (ps: with ps; [ requests flask sqlalchemy ])'
python3 -c "import flask; print(flask.__version__)" # Fonctionne !
# Shell pour le développement Rust
nix-shell -p rustc cargo rust-analyzer clippy rustfmt
# Shell pour le développement Go
nix-shell -p go gopls golangci-lint
# Script reproductible avec nix-shell shebang
cat << 'EOF' > script.py
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ps: with ps; [ requests beautifulsoup4 ])"
import requests
from bs4 import BeautifulSoup
resp = requests.get("https://example.com")
soup = BeautifulSoup(resp.text, 'html.parser')
print(soup.title.string)
EOF
chmod +x script.py
./script.py # Les dépendances sont automatiquement disponiblesFlakes : le futur de Nix
Les flakes sont le système moderne de composition de Nix, introduit pour résoudre les problèmes de reproductibilité du Nix classique. Un flake est un dépôt Nix avec deux fichiers clés :
- flake.nix : la définition déclarative du projet (entrées, sorties)
- flake.lock : le verrouillage des versions exactes de toutes les entrées (comme package-lock.json pour npm)
Les flakes garantissent que deux personnes exécutant le même flake obtiennent exactement le même résultat, peu importe leur machine ou le moment.
Anatomie d'un flake.nix
# flake.nix — Environnement de développement reproductible
# pour un projet Python/FastAPI avec PostgreSQL et Redis
{
description = "Environnement de développement pour mon-api-projet";
# === ENTRÉES ===
# Les sources externes dont dépend ce flake
inputs = {
# Nixpkgs : le dépôt de paquets
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
# Flake-utils : utilitaires pour simplifier les flakes multi-plateformes
flake-utils.url = "github:numtide/flake-utils";
# Poetry2nix : intégration Poetry/Nix pour les projets Python
poetry2nix = {
url = "github:nix-community/poetry2nix";
inputs.nixpkgs.follows = "nixpkgs"; # Réutiliser le même nixpkgs
};
};
# === SORTIES ===
# Ce que ce flake produit
outputs = { self, nixpkgs, flake-utils, poetry2nix }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true; # Autoriser les paquets non-libres si nécessaire
};
# Version de Python avec les packages nécessaires au développement
pythonEnv = pkgs.python312.withPackages (ps: with ps; [
# Frameworks
fastapi
uvicorn
sqlalchemy
alembic
pydantic
# Base de données
psycopg2
redis
# Outils de dev
pytest
pytest-cov
pytest-asyncio
black
ruff
mypy
ipython
# HTTP
httpx
requests
]);
in {
# === DEVSHELL ===
# L'environnement de développement, activé avec `nix develop`
devShells.default = pkgs.mkShell {
name = "mon-api-dev";
# Paquets disponibles dans le shell
buildInputs = [
# Python avec ses packages
pythonEnv
# Base de données
pkgs.postgresql_16
pkgs.redis
# Outils CLI
pkgs.curl
pkgs.jq
pkgs.httpie
pkgs.just # Task runner (alternative à Make)
pkgs.watchexec # Relancer une commande au changement de fichiers
# Infrastructure
pkgs.docker-compose
pkgs.terraform
pkgs.ansible
# Outils de qualité
pkgs.pre-commit
pkgs.shellcheck
pkgs.hadolint # Linter pour Dockerfile
# Git
pkgs.git
pkgs.git-crypt
pkgs.delta # Diff amélioré
];
# Variables d'environnement
env = {
DATABASE_URL = "postgresql://dev:dev@localhost:5432/myapp";
REDIS_URL = "redis://localhost:6379";
PYTHONDONTWRITEBYTECODE = "1";
PYTHONUNBUFFERED = "1";
};
# Script exécuté à l'entrée dans le shell
shellHook = ''
echo "╔══════════════════════════════════════════╗"
echo "║ Environnement de développement actif ║"
echo "║ Python: $(python3 --version) ║"
echo "║ PostgreSQL: $(pg_config --version) ║"
echo "╚══════════════════════════════════════════╝"
# Créer un virtualenv local pour les dépendances Python additionnelles
if [ ! -d .venv ]; then
echo "Création du virtualenv..."
python3 -m venv .venv
fi
source .venv/bin/activate
# Alias utiles
alias run="uvicorn app.main:app --reload --host 0.0.0.0 --port 8000"
alias test="pytest -v --cov=app tests/"
alias lint="ruff check . && mypy app/"
alias fmt="black . && ruff check --fix ."
alias db-migrate="alembic upgrade head"
alias db-revision="alembic revision --autogenerate -m"
'';
};
# === PACKAGES ===
# Le paquet buildable du projet
packages.default = pkgs.python312Packages.buildPythonApplication {
pname = "mon-api";
version = "1.0.0";
src = ./.;
propagatedBuildInputs = with pkgs.python312Packages; [
fastapi uvicorn sqlalchemy psycopg2 redis pydantic alembic
];
};
# === IMAGE DOCKER ===
# Construire une image Docker optimisée via Nix
packages.docker = pkgs.dockerTools.buildLayeredImage {
name = "mon-api";
tag = "latest";
contents = [ self.packages.${system}.default ];
config = {
Cmd = [ "${self.packages.${system}.default}/bin/mon-api" ];
ExposedPorts = { "8000/tcp" = {}; };
Env = [
"PYTHONUNBUFFERED=1"
];
};
};
}
);
}Utiliser un flake
# Entrer dans l'environnement de développement
nix develop
# Ou depuis n'importe où (sans cloner le repo)
nix develop github:user/mon-projet
# Mettre à jour les dépendances (flake.lock)
nix flake update
# Mettre à jour une seule entrée
nix flake update nixpkgs
# Construire le paquet
nix build
# Construire l'image Docker
nix build .#docker
docker load < result
# Lancer directement sans construire
nix run
# Afficher les sorties du flake
nix flake show
# Vérifier le flake
nix flake checkLe fichier flake.lock
Le fichier flake.lock est généré automatiquement et verrouille les versions exactes de toutes les entrées. C'est ce qui garantit la reproductibilité :
# flake.lock (extrait simplifié — format JSON)
# Ce fichier est géré automatiquement par Nix
# Il contient les hashes exacts de chaque entrée
# Afficher les entrées verrouillées
nix flake metadata
# Description: Environnement de développement pour mon-api-projet
# Path: /home/user/projects/mon-api
# Revision: abc123...
# Last modified: 2025-12-15
# Inputs:
# ├── flake-utils: github:numtide/flake-utils/...
# ├── nixpkgs: github:NixOS/nixpkgs/nixos-24.11/...
# └── poetry2nix: github:nix-community/poetry2nix/...
# Le flake.lock doit être committé dans Git
# pour que tous les développeurs aient les mêmes versions
git add flake.nix flake.lock
git commit -m "feat: add Nix flake for reproducible dev environment"NixOS : le système d'exploitation déclaratif
NixOS est une distribution Linux qui applique les principes de Nix à l'ensemble du système d'exploitation. Au lieu de configurer votre système avec une série de commandes impératives (apt install, systemctl enable, éditer /etc/nginx/nginx.conf...), vous décrivez l'état souhaité dans un fichier de configuration et NixOS s'occupe de le réaliser.
Le fichier configuration.nix
# /etc/nixos/configuration.nix
# Configuration NixOS complète pour un serveur web/API
{ config, pkgs, lib, ... }:
{
# === SYSTÈME DE BASE ===
system.stateVersion = "24.11";
# Boot loader
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Kernel
boot.kernelPackages = pkgs.linuxPackages_latest;
# === RÉSEAU ===
networking = {
hostName = "serveur-prod";
domain = "example.com";
# Interface réseau
interfaces.ens3 = {
ipv4.addresses = [{
address = "10.0.0.5";
prefixLength = 24;
}];
};
defaultGateway = "10.0.0.1";
nameservers = [ "1.1.1.1" "8.8.8.8" ];
# Pare-feu déclaratif
firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
allowedUDPPorts = [ ];
# Règles iptables personnalisées si nécessaire
extraCommands = ''
iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 5432 -j ACCEPT
'';
};
};
# === FUSEAU HORAIRE ET LOCALE ===
time.timeZone = "Europe/Paris";
i18n.defaultLocale = "fr_FR.UTF-8";
i18n.extraLocaleSettings = {
LC_TIME = "fr_FR.UTF-8";
LC_MONETARY = "fr_FR.UTF-8";
};
# === UTILISATEURS ===
users.users = {
admin = {
isNormalUser = true;
description = "Administrateur";
extraGroups = [ "wheel" "docker" "networkmanager" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExample... admin@workstation"
];
shell = pkgs.zsh;
};
deploy = {
isNormalUser = true;
description = "Compte de déploiement";
extraGroups = [ "docker" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExample... ci@github"
];
};
};
# Interdire la connexion root par mot de passe
users.users.root.hashedPassword = "!"; # Désactivé
# === SSH ===
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "no";
X11Forwarding = false;
MaxAuthTries = 3;
};
# Uniquement clés ED25519 et RSA 4096+
hostKeys = [
{ path = "/etc/ssh/ssh_host_ed25519_key"; type = "ed25519"; }
{ path = "/etc/ssh/ssh_host_rsa_key"; type = "rsa"; bits = 4096; }
];
};
# === PAQUETS SYSTÈME ===
environment.systemPackages = with pkgs; [
# Éditeurs
vim
neovim
helix
# Outils système
htop
btop
tmux
git
curl
wget
jq
yq
ripgrep
fd
bat
tree
ncdu
duf
# Réseau
dig
nmap
tcpdump
iperf3
mtr
# Conteneurs
docker
docker-compose
# Monitoring
prometheus-node-exporter
];
# === SERVICES ===
# Nginx comme reverse proxy
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
virtualHosts = {
"api.example.com" = {
forceSSL = true;
enableACME = true; # Let's Encrypt automatique
locations."/" = {
proxyPass = "http://127.0.0.1:8000";
proxyWebsockets = true;
extraConfig = ''
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
locations."/static/" = {
root = "/var/www/api";
extraConfig = ''
expires 30d;
add_header Cache-Control "public, immutable";
'';
};
};
"monitoring.example.com" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:3000"; # Grafana
};
# Accès restreint par IP
extraConfig = ''
allow 10.0.0.0/8;
deny all;
'';
};
};
};
# Let's Encrypt
security.acme = {
acceptTerms = true;
defaults.email = "admin@example.com";
};
# PostgreSQL
services.postgresql = {
enable = true;
package = pkgs.postgresql_16;
settings = {
max_connections = 200;
shared_buffers = "256MB";
effective_cache_size = "1GB";
work_mem = "4MB";
maintenance_work_mem = "128MB";
log_min_duration_statement = 1000; # Log les requêtes > 1s
};
authentication = ''
local all all trust
host all all 10.0.0.0/8 md5
'';
ensureDatabases = [ "myapp" "myapp_test" ];
ensureUsers = [
{
name = "myapp";
ensureDBOwnership = true;
}
];
};
# Redis
services.redis.servers.default = {
enable = true;
port = 6379;
bind = "127.0.0.1";
settings = {
maxmemory = "256mb";
maxmemory-policy = "allkeys-lru";
};
};
# Docker
virtualisation.docker = {
enable = true;
autoPrune = {
enable = true;
dates = "weekly";
flags = [ "--all" "--volumes" ];
};
};
# Fail2ban
services.fail2ban = {
enable = true;
maxretry = 5;
bantime = "1h";
bantime-increment.enable = true;
};
# Automatic garbage collection
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
# Automatic system updates
system.autoUpgrade = {
enable = true;
allowReboot = false;
dates = "04:00";
flake = "github:myorg/nixos-config";
};
# === NIX SETTINGS ===
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
auto-optimise-store = true;
# Cache binaire pour accélérer les builds
substituters = [
"https://cache.nixos.org"
"https://myorg.cachix.org"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"myorg.cachix.org-1:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="
];
};
# Autoriser les paquets non-libres
nixpkgs.config.allowUnfree = true;
# === PROGRAMMES ===
programs.zsh.enable = true;
programs.mtr.enable = true;
programs.gnupg.agent.enable = true;
}Appliquer la configuration
# Reconstruire le système avec la nouvelle configuration
sudo nixos-rebuild switch
# Tester la configuration sans l'activer au reboot
sudo nixos-rebuild test
# Construire sans activer (vérifier que ça compile)
sudo nixos-rebuild build
# Reconstruire avec un flake distant
sudo nixos-rebuild switch --flake github:myorg/nixos-config#serveur-prod
# Lister les générations du système
nix-env --list-generations --profile /nix/var/nix/profiles/system
# 42 2025-11-15 14:30:00 (current)
# 41 2025-11-10 09:15:00
# 40 2025-11-05 16:45:00
# Rollback à la génération précédente
sudo nixos-rebuild switch --rollback
# Ou choisir une génération spécifique au boot (menu GRUB/systemd-boot)
# Chaque génération est listée dans le menu de démarrage !Rollbacks atomiques et générations
L'un des avantages les plus puissants de NixOS est le système de générations. Chaque fois que vous exécutez nixos-rebuild switch, une nouvelle génération est créée. Vous pouvez :
- Lister toutes les générations avec leur date et les changements
- Revenir à n'importe quelle génération instantanément avec
nixos-rebuild switch --rollback - Choisir une génération au boot dans le menu du bootloader
- Différencier les générations pour voir ce qui a changé
Cela signifie qu'une mise à jour qui casse le système n'est jamais un problème irréversible. Un simple reboot avec sélection de la génération précédente restaure un système fonctionnel en quelques secondes.
# Voir les différences entre deux générations
nix store diff-closures /nix/var/nix/profiles/system-41-link /nix/var/nix/profiles/system-42-link
# nginx: 1.26.2 → 1.27.3
# postgresql: 16.3 → 16.4
# linux: 6.11.5 → 6.12.1
# openssl: 3.3.1 → 3.3.2 (CVE-2024-xxxx fixed)
# Nettoyer les anciennes générations (garder les 5 dernières)
sudo nix-collect-garbage --delete-older-than 5d
# Ou garder un nombre spécifique de générations
sudo nix-env --delete-generations +5 --profile /nix/var/nix/profiles/systemHome Manager : les dotfiles déclaratifs
Home Manager est un outil qui applique les principes de Nix à la configuration de l'environnement utilisateur. Au lieu de gérer vos dotfiles manuellement (ou avec des scripts d'installation fragiles), vous les déclarez dans un fichier Nix et Home Manager s'occupe de les déployer.
# home.nix — Configuration Home Manager
{ config, pkgs, ... }:
{
home.username = "admin";
home.homeDirectory = "/home/admin";
home.stateVersion = "24.11";
# === PAQUETS UTILISATEUR ===
home.packages = with pkgs; [
# CLI modernes
eza # Remplacement de ls
bat # Remplacement de cat
ripgrep # Remplacement de grep
fd # Remplacement de find
delta # Diff amélioré pour Git
fzf # Fuzzy finder
zoxide # Remplacement de cd (auto-jump)
starship # Prompt shell moderne
lazygit # TUI pour Git
yazi # File manager TUI
# Développement
neovim
direnv
just
watchexec
# Kubernetes
kubectl
kubectx
k9s
helm
kustomize
# Cloud
awscli2
terraform
];
# === GIT ===
programs.git = {
enable = true;
userName = "Mon Nom";
userEmail = "mon@email.com";
signing = {
key = "~/.ssh/id_ed25519.pub";
signByDefault = true;
};
delta = {
enable = true;
options = {
navigate = true;
light = false;
side-by-side = true;
line-numbers = true;
};
};
extraConfig = {
init.defaultBranch = "main";
pull.rebase = true;
push.autoSetupRemote = true;
rerere.enabled = true;
merge.conflictStyle = "zdiff3";
diff.algorithm = "histogram";
};
aliases = {
st = "status -sb";
co = "checkout";
br = "branch";
ci = "commit";
lg = "log --graph --oneline --decorate --all";
undo = "reset --soft HEAD~1";
amend = "commit --amend --no-edit";
};
};
# === ZSH ===
programs.zsh = {
enable = true;
autosuggestion.enable = true;
syntaxHighlighting.enable = true;
history = {
size = 50000;
save = 50000;
ignoreDups = true;
ignoreAllDups = true;
share = true;
};
shellAliases = {
ls = "eza --icons --group-directories-first";
ll = "eza -la --icons --group-directories-first";
lt = "eza --tree --icons --level=3";
cat = "bat";
grep = "rg";
find = "fd";
cd = "z"; # zoxide
".." = "cd ..";
"..." = "cd ../..";
k = "kubectl";
kx = "kubectx";
kn = "kubens";
tf = "terraform";
dc = "docker compose";
lg = "lazygit";
};
initExtra = ''
# Charger les completions Kubernetes
source <(kubectl completion zsh)
# Direnv hook
eval "$(direnv hook zsh)"
'';
};
# === STARSHIP (prompt) ===
programs.starship = {
enable = true;
settings = {
add_newline = true;
format = "$all";
character = {
success_symbol = "[➜](bold green)";
error_symbol = "[✗](bold red)";
};
git_branch.symbol = " ";
python.symbol = " ";
nodejs.symbol = " ";
rust.symbol = " ";
nix_shell = {
symbol = " ";
format = "via [$symbol$name]($style) ";
};
kubernetes = {
disabled = false;
symbol = "⎈ ";
};
};
};
# === DIRENV ===
programs.direnv = {
enable = true;
nix-direnv.enable = true; # Intégration avec Nix flakes
};
# === TMUX ===
programs.tmux = {
enable = true;
terminal = "tmux-256color";
shell = "${pkgs.zsh}/bin/zsh";
keyMode = "vi";
baseIndex = 1;
escapeTime = 0;
historyLimit = 50000;
mouse = true;
extraConfig = ''
# Split panes avec | et -
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# Navigation vim-like entre panes
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# Status bar
set -g status-style bg=default,fg=white
set -g status-left "#[fg=green]#S "
set -g status-right "#[fg=yellow]%H:%M #[fg=cyan]%d-%m-%Y"
'';
};
# === FZF ===
programs.fzf = {
enable = true;
enableZshIntegration = true;
defaultCommand = "fd --type f --hidden --follow --exclude .git";
defaultOptions = [
"--height 40%"
"--layout=reverse"
"--border"
"--info=inline"
];
};
# === FICHIERS DE CONFIGURATION ARBITRAIRES ===
home.file = {
".config/k9s/skin.yml".text = ''
k9s:
body:
bgColor: default
fgColor: white
'';
};
# Laisser Home Manager gérer lui-même
programs.home-manager.enable = true;
}# Installer Home Manager (standalone, sans NixOS)
nix-channel --add https://github.com/nix-community/home-manager/archive/release-24.11.tar.gz home-manager
nix-channel --update
nix-shell '<home-manager>' -A install
# Ou avec les flakes (recommandé)
nix run home-manager/release-24.11 -- init --switch
# Appliquer la configuration
home-manager switch
# Lister les générations Home Manager
home-manager generations
# Rollback
home-manager generations # noter le numéro
# Activer une génération précédente
/nix/var/nix/profiles/per-user/admin/home-manager-XX-link/activatedevenv.sh : Nix simplifié pour le développement
devenv.sh est un outil construit sur Nix qui simplifie radicalement la création d'environnements de développement. Si vous trouvez le langage Nix intimidant, devenv est votre porte d'entrée :
# devenv.nix — Configuration devenv pour un projet web
{ pkgs, lib, config, ... }:
{
# Paquets disponibles dans l'environnement
packages = [
pkgs.git
pkgs.jq
pkgs.httpie
pkgs.just
];
# Langages avec gestion automatique des versions
languages.python = {
enable = true;
version = "3.12";
poetry = {
enable = true;
activate.enable = true;
};
};
languages.javascript = {
enable = true;
package = pkgs.nodejs_22;
pnpm.enable = true;
};
# Services locaux (démarrés avec `devenv up`)
services.postgres = {
enable = true;
package = pkgs.postgresql_16;
initialDatabases = [
{ name = "myapp_dev"; }
{ name = "myapp_test"; }
];
listen_addresses = "127.0.0.1";
port = 5432;
};
services.redis = {
enable = true;
port = 6379;
};
# Processus personnalisés (démarrés avec `devenv up`)
processes = {
api.exec = "uvicorn app.main:app --reload --port 8000";
worker.exec = "celery -A app.tasks worker --loglevel=info";
frontend.exec = "cd frontend && pnpm dev";
};
# Scripts personnalisés (disponibles comme commandes)
scripts = {
migrate.exec = "alembic upgrade head";
seed.exec = "python scripts/seed_data.py";
test.exec = "pytest -v --cov=app tests/";
lint.exec = "ruff check . && mypy app/";
};
# Pre-commit hooks
pre-commit.hooks = {
ruff.enable = true;
mypy.enable = true;
nixfmt-rfc-style.enable = true;
shellcheck.enable = true;
commitizen.enable = true;
};
# Variables d'environnement
env = {
DATABASE_URL = "postgresql://localhost:5432/myapp_dev";
REDIS_URL = "redis://localhost:6379";
DEBUG = "true";
};
# Message d'entrée
enterShell = ''
echo "🔧 Environnement de développement prêt !"
echo ""
echo "Commandes disponibles :"
echo " devenv up - Démarrer tous les services"
echo " migrate - Appliquer les migrations"
echo " seed - Peupler la base de données"
echo " test - Lancer les tests"
echo " lint - Vérifier le code"
'';
}# Installer devenv
nix profile install --accept-flake-config github:cachix/devenv/latest
# Initialiser dans un projet existant
devenv init
# Entrer dans l'environnement
devenv shell
# Démarrer tous les services (PostgreSQL, Redis, API, frontend...)
devenv up
# Lancer les tests (script personnalisé)
devenv shell -- test
# Mettre à jour les dépendances
devenv updateCachix : le cache binaire
L'un des reproches fréquents à Nix est le temps de compilation. Quand un paquet n'est pas disponible en cache binaire, Nix le compile depuis les sources, ce qui peut prendre beaucoup de temps. Cachix résout ce problème en fournissant un service de cache binaire partagé :
# Installer Cachix
nix-env -iA cachix -f https://cachix.org/api/v1/install
# S'authentifier
cachix authtoken YOUR_TOKEN
# Créer un cache pour votre organisation
cachix create myorg
# Pousser les résultats de build vers le cache
nix build .#default | cachix push myorg
# Configurer Nix pour utiliser votre cache
# Dans /etc/nix/nix.conf ou configuration.nix :
# substituters = https://cache.nixos.org https://myorg.cachix.org
# trusted-public-keys = cache.nixos.org-1:... myorg.cachix.org-1:...
# Dans un flake, utiliser nixConfig pour configurer le cache
# (dans flake.nix)
# nixConfig = {
# extra-substituters = [ "https://myorg.cachix.org" ];
# extra-trusted-public-keys = [ "myorg.cachix.org-1:..." ];
# };
# Intégration CI : pousser automatiquement après chaque build
# GitHub Actions :
# - name: Push to Cachix
# run: |
# nix build .#default
# cachix push myorg ./resultImages Docker depuis Nix
Nix peut construire des images Docker extrêmement optimisées, sans Docker lui-même, et sans les couches inutiles d'une image basée sur une distribution :
# Image Docker construite avec Nix
# Résultat : image minimale contenant UNIQUEMENT ce qui est nécessaire
{ pkgs ? import <nixpkgs> {} }:
pkgs.dockerTools.buildLayeredImage {
name = "mon-api";
tag = "latest";
# Contenu de l'image (uniquement les paquets nécessaires)
contents = [
pkgs.python312
pkgs.python312Packages.fastapi
pkgs.python312Packages.uvicorn
pkgs.cacert # Certificats SSL
pkgs.busybox # Utilitaires de base (optionnel, pour le debug)
];
# Configuration de l'image
config = {
Cmd = [ "uvicorn" "app.main:app" "--host" "0.0.0.0" "--port" "8000" ];
ExposedPorts = { "8000/tcp" = {}; };
Env = [
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
"PYTHONUNBUFFERED=1"
];
WorkingDir = "/app";
};
# Copier le code source de l'application
extraCommands = ''
mkdir -p app
cp -r ${./src}/* app/
'';
}# Construire l'image
nix build -f docker.nix
# Le résultat est un fichier tar
ls -lh result
# -r--r--r-- 1 root root 85M result (vs ~500MB pour une image Python classique !)
# Charger l'image dans Docker
docker load < result
# Vérifier
docker images mon-api
# REPOSITORY TAG IMAGE ID CREATED SIZE
# mon-api latest abc123def456 53 years ago 85MB
# Note : "53 years ago" = timestamp 0 pour la reproductibilité
# Lancer
docker run -p 8000:8000 mon-apiLes avantages des images Docker construites avec Nix :
- Taille minimale : seuls les paquets nécessaires sont inclus (pas de gestionnaire de paquets, pas de shell inutile, pas de man pages)
- Reproductibilité : le même flake.lock = la même image, bit pour bit
- Sécurité : surface d'attaque réduite (pas de coreutils inutiles, pas de shell par défaut)
- Pas besoin de Docker pour construire : Nix construit l'image sans daemon Docker
- Couches optimisées :
buildLayeredImagecrée des couches intelligentes pour maximiser la réutilisation du cache
Déploiement avec NixOS
Plusieurs outils permettent de déployer des configurations NixOS sur des machines distantes :
deploy-rs
deploy-rs est l'outil de déploiement le plus populaire pour NixOS. Il est simple, rapide, et supporte le rollback automatique :
# flake.nix avec deploy-rs
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
deploy-rs.url = "github:serokell/deploy-rs";
};
outputs = { self, nixpkgs, deploy-rs }: {
# Configuration NixOS pour chaque serveur
nixosConfigurations = {
web-1 = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./hosts/web-1/configuration.nix ];
};
web-2 = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./hosts/web-2/configuration.nix ];
};
db-1 = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./hosts/db-1/configuration.nix ];
};
};
# Configuration de déploiement
deploy.nodes = {
web-1 = {
hostname = "10.0.0.10";
profiles.system = {
user = "root";
sshUser = "deploy";
path = deploy-rs.lib.x86_64-linux.activate.nixos
self.nixosConfigurations.web-1;
# Rollback automatique si le serveur ne répond plus après 60s
autoRollback = true;
magicRollback = true;
};
};
web-2 = {
hostname = "10.0.0.11";
profiles.system = {
user = "root";
sshUser = "deploy";
path = deploy-rs.lib.x86_64-linux.activate.nixos
self.nixosConfigurations.web-2;
autoRollback = true;
magicRollback = true;
};
};
db-1 = {
hostname = "10.0.0.20";
profiles.system = {
user = "root";
sshUser = "deploy";
path = deploy-rs.lib.x86_64-linux.activate.nixos
self.nixosConfigurations.db-1;
autoRollback = true;
magicRollback = true;
};
};
};
# Vérifications
checks = builtins.mapAttrs
(system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
};
}# Déployer tous les serveurs
nix run github:serokell/deploy-rs -- .
# Déployer un serveur spécifique
nix run github:serokell/deploy-rs -- .#web-1
# Déployer avec dry-run
nix run github:serokell/deploy-rs -- . -- --dry-activate
# Le magic rollback fonctionne ainsi :
# 1. deploy-rs active la nouvelle configuration
# 2. Un timer de 60 secondes démarre
# 3. Si le déploiement réussit (confirmé via SSH), le timer est annulé
# 4. Si le serveur est inaccessible, le timer expire et le rollback est effectuéColmena
Colmena est une alternative à deploy-rs, inspirée de NixOps mais plus moderne et plus simple :
# Déployer avec Colmena
colmena apply
# Déployer un nœud spécifique
colmena apply --on web-1
# Build sans déployer
colmena build
# Exécuter une commande sur tous les nœuds
colmena exec -- systemctl status nginxComparaison avec les alternatives
Nix vs Docker
Nix et Docker résolvent des problèmes différents mais complémentaires :
| Aspect | Nix | Docker |
|---|---|---|
| Objectif principal | Reproductibilité des builds et environnements | Isolation et portabilité des applications |
| Granularité | Paquet par paquet | Conteneur entier (image) |
| Reproductibilité | Bit-à-bit (avec flake.lock) | Approximative (même Dockerfile peut produire des images différentes) |
| Dev environment | Natif (nix develop, devenv) | Nécessite des montages volumes, rebuild d'images |
| Performance dev | Natif (pas de couche VM/conteneur) | Overhead I/O (surtout sur macOS) |
| Déploiement prod | NixOS ou images Docker construites par Nix | Standard de l'industrie |
| Courbe d'apprentissage | Raide (langage Nix) | Modérée (Dockerfile simple) |
En pratique, beaucoup d'équipes utilisent Nix ET Docker ensemble : Nix pour construire des images Docker reproductibles et pour les environnements de développement, Docker/Kubernetes pour le déploiement en production.
Nix vs Ansible
| Aspect | Nix/NixOS | Ansible |
|---|---|---|
| Paradigme | Déclaratif pur | Impératif avec déclaration d'état souhaité |
| Reproductibilité | Garantie (même entrées = même résultat) | Best-effort (dépend de l'état initial) |
| Rollback | Atomique et instantané | Pas natif (il faut écrire des playbooks de rollback) |
| Idempotence | Garantie par construction | Responsabilité du développeur de playbooks |
| Systèmes cibles | NixOS uniquement (ou Nix sur d'autres distros) | N'importe quel Linux (et Windows, macOS, network devices) |
| Adoption | Communauté en croissance | Standard de l'industrie, très large adoption |
Nix vs Guix
GNU Guix est un projet similaire à Nix, basé sur les mêmes principes (gestionnaire de paquets fonctionnel, reproductibilité), mais avec des différences notables :
- Langage : Guix utilise Guile Scheme (un vrai langage de programmation) au lieu du langage Nix custom
- Philosophie : Guix est un projet GNU, strictement libre (pas de paquets non-libres par défaut)
- Taille : Nixpkgs (~100k paquets) est significativement plus grand que Guix (~30k paquets)
- Communauté : Nix a une communauté plus large et une adoption industrielle plus importante
- Écosystème : Nix a plus d'outils tiers (Home Manager, devenv, Cachix, deploy-rs, etc.)
Courbe d'apprentissage et conseils
Soyons honnêtes : la courbe d'apprentissage de Nix est raide. Le langage Nix est un langage fonctionnel lazy-evaluated avec une syntaxe unique, la documentation est parfois fragmentée, et les messages d'erreur peuvent être cryptiques. Voici comment aborder l'apprentissage :
Phase 1 : Utilisateur (semaine 1-2)
- Installez Nix (pas NixOS, juste le gestionnaire de paquets) sur votre distribution actuelle
- Utilisez
nix shellpour tester des outils sans les installer (nix shell nixpkgs#ripgrep) - Utilisez
nix runpour exécuter des programmes (nix run nixpkgs#btop) - Essayez devenv.sh sur un projet existant — c'est la porte d'entrée la plus accessible
Phase 2 : Développeur (semaine 3-6)
- Apprenez les bases du langage Nix : nix.dev est la meilleure ressource pour débuter
- Créez un flake.nix pour un de vos projets (utilisez les templates :
nix flake init -t templates#trivial) - Configurez direnv + nix-direnv pour activer automatiquement l'environnement Nix en entrant dans un répertoire
- Explorez Nixpkgs : search.nixos.org pour chercher des paquets et des options NixOS
Phase 3 : Avancé (mois 2-6)
- Essayez NixOS dans une VM d'abord, puis sur une machine secondaire
- Configurez Home Manager pour gérer vos dotfiles
- Construisez des images Docker avec Nix
- Écrivez vos propres derivations pour packager vos applications
- Configurez Cachix pour partager les builds avec votre équipe
Ressources d'apprentissage recommandées
- nix.dev : la documentation officielle réécrite, moderne et bien structurée
- zero-to-nix.com : tutorial interactif pour les débutants (par Determinate Systems)
- nixos.wiki : wiki communautaire avec des exemples pratiques
- Nix Pills : série d'articles approfondis sur le fonctionnement interne de Nix
- search.nixos.org : moteur de recherche pour les paquets et les options NixOS
- NixOS Discourse : forum communautaire actif et accueillant
Quand adopter Nix ?
Nix n'est pas pour tout le monde ni pour tous les contextes. Voici des critères pour décider :
Adoptez Nix si :
- Vous avez des problèmes récurrents de reproductibilité (« ça marche sur ma machine »)
- Votre projet utilise plusieurs langages/runtimes et la gestion des versions est un cauchemar
- Vous voulez des environnements de développement identiques pour toute l'équipe
- Vous construisez des images Docker et voulez une reproductibilité parfaite
- Vous gérez des serveurs et voulez des rollbacks atomiques
- Vous êtes prêt à investir du temps d'apprentissage pour des bénéfices à long terme
- Votre équipe est à l'aise avec la programmation fonctionnelle
Peut-être pas encore Nix si :
- Votre projet est simple avec peu de dépendances
- Votre équipe n'a pas le temps d'investir dans l'apprentissage
- Vous avez besoin de support commercial (bien que Determinate Systems propose du support)
- Votre infrastructure est principalement Windows
- Vous êtes satisfait de Docker + Ansible pour vos besoins actuels
L'écosystème Nix en 2025-2026
L'écosystème Nix est en pleine effervescence :
- Determinate Systems : entreprise fondée par des contributeurs majeurs de Nix, proposant des outils commerciaux (FlakeHub, Determinate Nix Installer) et du support entreprise
- Flox : outil qui simplifie l'utilisation de Nix pour les équipes de développement, avec une interface familière
- Tvix : réécriture de l'évaluateur Nix en Rust, pour de meilleures performances et un meilleur support des erreurs
- Lix : fork communautaire de Nix focalisé sur la stabilité et la gouvernance ouverte
- NixOS Foundation : restructuration de la gouvernance pour une meilleure représentation de la communauté
- Adoption croissante : de plus en plus d'entreprises (Shopify, Replit, Determinate Systems, Flox, Cachix, Hercules CI) utilisent et contribuent à Nix
- Intégration GitHub : les GitHub Codespaces et Actions supportent de mieux en mieux Nix
Direnv + Nix : l'expérience développeur optimale
La combinaison de direnv et nix-direnv crée une expérience développeur exceptionnelle : quand vous entrez dans un répertoire contenant un flake.nix, l'environnement de développement est automatiquement activé :
# Installer direnv et nix-direnv
nix profile install nixpkgs#direnv nixpkgs#nix-direnv
# Ajouter à votre .zshrc ou .bashrc
eval "$(direnv hook zsh)"
# Dans votre projet, créer un .envrc
echo "use flake" > .envrc
direnv allow
# Maintenant, chaque fois que vous entrez dans le répertoire :
cd mon-projet/
# direnv: loading .envrc
# direnv: using flake
# (tous les outils de votre flake.nix sont disponibles)
python3 --version # La version définie dans flake.nix
node --version # La version définie dans flake.nix
cd ..
# direnv: unloading
python3 --version # La version système (ou "command not found")Le cache de nix-direnv fait que les activations suivantes sont instantanées (pas de rebuild). C'est la meilleure façon d'utiliser Nix au quotidien pour le développement.
Conclusion
Nix et NixOS représentent un changement de paradigme dans la gestion des logiciels et des systèmes. En appliquant les principes de la programmation fonctionnelle — pureté, immutabilité, composition — à la gestion de paquets et à la configuration système, ils résolvent des problèmes fondamentaux que les outils traditionnels ne peuvent qu'atténuer.
Les bénéfices concrets sont considérables :
- Reproductibilité garantie : même flake.lock = même environnement, sur n'importe quelle machine
- Fin du « ça marche sur ma machine » : chaque développeur a exactement le même environnement
- Rollbacks atomiques : une mise à jour qui casse le système ? Un rollback instantané restaure l'état précédent
- Coexistence de versions : Python 3.10 et 3.12, Node 20 et 22, sur la même machine, sans conflit
- Images Docker minimales : construites par Nix, reproductibles, et une fraction de la taille des images classiques
- Infrastructure as Code véritablement déclaratif : NixOS décrit l'état souhaité, pas les étapes pour y arriver
La courbe d'apprentissage est réelle, mais l'investissement est rentable. Commencez par installer Nix sur votre machine actuelle (sans passer à NixOS), utilisez nix shell et nix develop pour vos projets, et explorez devenv.sh pour une introduction en douceur. Une fois que vous aurez goûté à la reproductibilité de Nix, il sera difficile de revenir en arrière.
En 2025-2026, avec la maturation des flakes, la croissance de Nixpkgs, les outils comme devenv et Home Manager, et l'investissement croissant de l'industrie, Nix n'est plus un outil de niche pour les passionnés. C'est une technologie de production, utilisée par des entreprises à travers le monde pour construire des systèmes fiables et reproductibles.
Pour aller plus loin : commencez par zero-to-nix.com pour une introduction interactive, puis explorez nix.dev pour la documentation approfondie. Le moteur de recherche NixOS est votre meilleur ami pour trouver des paquets et des options de configuration. Et rejoignez la communauté sur le NixOS Discourse — elle est accueillante et passionnée.