Aller au contenu
Sécurité medium

NixOS : installer et évaluer un premier système dans une VM KVM

28 min de lecture

Logo NixOS

NixOS est une distribution Linuxtout le système se déclare dans des fichiers de configuration : du bootloader aux services, en passant par les utilisateurs et les paquets. Ce premier lab vous guide pas à pas pour installer NixOS dans une VM KVM, valider une configuration déclarative avec flakes et disko, puis appliquer votre première modification. À la fin, vous disposez d’une machine reproductible prête pour les labs suivants.

Ce guide a été réécrit pour devenir le premier épisode d’une série. L’objectif est d’évaluer NixOS dans un environnement isolé avant d’envisager une installation sur un vrai poste. Les bases du gestionnaire de paquets Nix — écosystème, store, langage, flakes — sont désormais couvertes dans la section Nix dédiée. Cette page reste la porte d’entrée pratique côté NixOS.

  • Créer une VM KVM avec les bons paramètres pour NixOS (UEFI, virtio, RAM)
  • Installer NixOS avec un partitionnement déclaratif via disko et une configuration pilotée par flakes
  • Comprendre le rôle de flake.nix, configuration.nix et disko-config.nix
  • Valider le système installé : boot, réseau, SSH, paquets, configuration déclarative
  • Appliquer une première modification et observer le mécanisme de générations
  • Revenir en arrière en cas de problème grâce au rollback intégré

Vous avez entendu parler de NixOS comme d’un système « immuable » et « reproductible », mais vous hésitez à l’installer directement sur votre machine. C’est normal : installer un OS aussi différent sans filet est risqué.

Ce lab répond à des situations concrètes :

  • Vous voulez évaluer NixOS avant de décider si vous l’adoptez
  • Vous préparez un bastion ou un poste d’administration et vous cherchez un OS reproductible et auditable
  • Vous voulez comprendre comment fonctionne la configuration déclarative sans casser votre environnement de travail
  • Vous souhaitez tester des modifications (paquets, services, durcissement) dans un environnement jetable et recommençable

Monter une VM locale pour évaluer NixOS présente plusieurs avantages concrets :

  • Zéro risque : votre poste de travail reste intact, vous pouvez tout supprimer et recommencer
  • Reproductibilité : la VM se recrée à l’identique à partir des fichiers de configuration
  • Itération rapide : tester une modification prend quelques minutes, pas une réinstallation complète
  • Préparation de la suite : cette VM servira de base pour les labs suivants (réparation, durcissement, secrets, Secure Boot)
  • Environnement réaliste : contrairement à un conteneur, une VM KVM expose un vrai cycle de boot UEFI, un vrai partitionnement disque et un vrai systemd

Ce lab utilise des fichiers de configuration écrits en langage Nix et organisés autour d’un flake. Vous n’avez pas besoin de maîtriser tous les détails, mais quelques notions facilitent la compréhension.

Nix n’est pas seulement un gestionnaire de paquets

Section intitulée « Nix n’est pas seulement un gestionnaire de paquets »

Nix est un écosystème complet : un gestionnaire de paquets fonctionnel, un langage dédié et un modèle de construction reproductible. NixOS pousse cette logique jusqu’au système d’exploitation entier. Tout ce que vous déclarez dans vos fichiers .nix — paquets, services, utilisateurs, bootloader — est construit et activé par Nix.

Si vous découvrez Nix, commencez par le guide Nix : comprendre l’écosystème.

Chaque paquet et chaque configuration système sont stockés dans /nix/store/ sous un chemin unique basé sur un hash cryptographique. Quand vous modifiez la configuration et reconstruisez le système, NixOS crée une nouvelle génération sans toucher à l’ancienne. Vous pouvez revenir en arrière instantanément.

Pour comprendre ce mécanisme en détail : Nix store, profils et générations.

Le fichier configuration.nix est une fonction Nix qui prend des arguments (config, pkgs, etc.) et retourne un attrset (ensemble d’attributs). La syntaxe utilise des listes [ ], des accolades { }, des points-virgules et des points pour naviguer dans les options.

Si la syntaxe vous paraît nouvelle, consultez le guide Apprendre les bases du langage Nix.

Les flakes résolvent un problème fondamental : sans eux, le résultat d’une construction dépend de la version de nixpkgs installée sur votre machine. Deux personnes obtiennent des systèmes différents. Avec un flake, les versions exactes sont verrouillées dans flake.lock, comme un package-lock.json pour npm.

Pour approfondir : Comprendre et utiliser les flakes Nix.

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

  • Un poste Linux avec KVM/QEMU et libvirt installés (virsh, virt-install)
  • Le firmware OVMF pour le boot UEFI (/usr/share/OVMF/OVMF_CODE_4M.fd sur Debian/Ubuntu)
  • 8 Go de RAM minimum à allouer à la VM (4 Go ne suffisent pas — Nix évalue en tmpfs)
  • 20 Go d’espace disque pour l’image QCOW2
  • L’ISO NixOS 25.11 minimal (environ 1,5 Go)
  • Une clé SSH existante sur l’hôte (pour l’accès sans mot de passe)
  • Nix installé sur l’hôte n’est pas nécessaire — tout se passe dans la VM

Une VM locale avec :

  • Un système NixOS 25.11 installé en UEFI
  • Un partitionnement déclaratif via disko (ESP 512 Mo + racine ext4)
  • Un accès SSH par clé (pas de mot de passe)
  • Des paquets de base : vim, git, htop, curl, tmux
  • Les flakes activés pour la reproductibilité
  • Le garbage collection automatique configuré

Architecture du lab NixOS — VM KVM avec partitionnement disko et configuration flake

FichierRôle
flake.nixPoint d’entrée : déclare les dépendances (nixpkgs, disko) et la configuration NixOS
configuration.nixConfiguration système : bootloader, réseau, utilisateurs, paquets, services
disko-config.nixSchéma de partitionnement déclaratif du disque
flake.lockVersions verrouillées de toutes les dépendances (généré automatiquement)

Avant de passer à l’installation, prenons le temps de comprendre chaque fichier. Vous ne les rédigerez pas de zéro — les voici prêts à l’emploi — mais savoir ce qu’ils font est essentiel pour la suite.

Le flake est le point d’entrée du projet. Il déclare deux choses :

  • les inputs : les dépendances avec leurs versions (ici nixpkgs 25.11 et disko)
  • les outputs : ce que le flake produit (ici une configuration NixOS nommée nixos-lab)
flake.nix
{
description = "NixOS Lab — VM KVM de test";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
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 notre configuration. Cela évite d’avoir deux versions différentes en mémoire.

Pour en savoir plus sur la structure d’un flake : Comprendre et utiliser les flakes Nix.

Disko décrit le partitionnement du disque de manière déclarative. Plus besoin de lancer fdisk ou parted manuellement :

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/KVM. Sur une machine physique, adaptez selon votre matériel (/dev/nvme0n1, /dev/sda).

C’est le cœur de la configuration NixOS. Chaque ligne correspond à quelque chose de concret sur le système :

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;
# Modules noyau pour la virtualisation
boot.initrd.availableKernelModules =
[ "virtio_pci" "virtio_blk" "virtio_scsi" "ahci" "sd_mod" ];
boot.kernelModules = [ "virtio_net" ];
# Réseau
networking.hostName = "nixos-lab";
# Locale et timezone
time.timeZone = "Europe/Paris";
i18n.defaultLocale = "fr_FR.UTF-8";
console.keyMap = "fr";
# SSH sécurisé
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = false;
};
};
# Utilisateur lab avec accès SSH par clé
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;
# Paquets de base
environment.systemPackages = with pkgs; [
vim git htop curl tmux
];
# Activer flakes et nix-command
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Garbage collection automatique
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
system.stateVersion = "25.11";
}

Quelques points importants :

  • imports = [ qemu-guest.nix ] : charge automatiquement les modules noyau nécessaires pour une VM KVM. Sans cette ligne, le système ne trouvera pas le disque au démarrage
  • services.openssh.enable = true : génère /etc/ssh/sshd_config, crée le service systemd et l’active au boot — tout en une ligne
  • users.users.lab : crée l’utilisateur, l’ajoute au groupe wheel et injecte la clé SSH publique
  • system.stateVersion : indique la version NixOS utilisée lors de l’installation initiale. Ne pas modifier après installation

Si la syntaxe { config, pkgs, ... }: ou les with pkgs vous sont étrangers, consultez le guide sur le langage Nix.

Le flake assemble le tout : il charge le module disko (pour le partitionnement), puis disko-config.nix (le schéma disque) et configuration.nix (le système). Lors de l’installation, NixOS utilise ces trois fichiers pour construire un système complet et cohérent.

flake.nix
├── input: nixpkgs 25.11
├── input: disko
└── output: nixosConfigurations.nixos-lab
├── disko.nixosModules.disko (module disko)
├── ./disko-config.nix (partitionnement)
└── ./configuration.nix (système)

Récupérez l’ISO minimale NixOS 25.11 :

Fenêtre de terminal
mkdir -p ~/lab-nixos/images && cd ~/lab-nixos/images
wget https://channels.nixos.org/nixos-25.11/latest-nixos-minimal-x86_64-linux.iso

L’ISO fait environ 1,5 Go. Elle contient un système live avec Nix préinstallé et les flakes activés.

Fenêtre de terminal
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é. L’installation finale occupe environ 3 Go.

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 : la VM doit démarrer sur l’ISO et afficher un shell root.

Fenêtre de terminal
virsh list --all
# Id Nom État
# -- --------- -----------
# 1 nixos-lab running

L’ISO NixOS démarre avec un utilisateur root sans mot de passe, mais SSH refuse les connexions sans mot de passe. Il faut en définir un temporairement via la console :

Fenêtre de terminal
# Attendre ~40 secondes que l'ISO boot, puis définir un mot de passe
virsh send-key nixos-lab KEY_P KEY_A KEY_S KEY_S KEY_W KEY_D KEY_ENTER
sleep 2
# Taper le mot de passe "nixos" (deux fois)
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

Récupérez l’adresse IP de la VM, puis connectez-vous :

Fenêtre de terminal
# Trouver l'IP attribuée par DHCP
virsh domifaddr nixos-lab
# Se connecter en SSH
sshpass -p 'nixos' ssh -o StrictHostKeyChecking=no root@192.168.122.x

Sur l’hôte, créez le répertoire du projet et les trois fichiers :

Fenêtre de terminal
mkdir -p ~/lab-nixos/nixos-config && cd ~/lab-nixos/nixos-config

Créez flake.nix, disko-config.nix et configuration.nix avec le contenu présenté dans la section précédente.

Copiez les fichiers dans la VM :

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

Initialiser le dépôt Git (obligatoire pour les flakes)

Section intitulée « Initialiser le dépôt Git (obligatoire pour les flakes) »

Dans la VM, les flakes exigent un dépôt Git pour déterminer quels fichiers inclure. Sans Git, l’installation échoue avec une erreur cryptique :

error: assertion '!(store->isInStore(path))' failed at
src/libfetchers/input-accessor.cc (copyInputToStore)
Fenêtre de terminal
# Dans la VM (SSH)
cd /tmp/nixos-config
git init
git config user.email "lab@nixos-lab"
git config user.name "lab"
git add .
git commit -m "initial nixos config"
Fenêtre de terminal
nix flake lock
git add flake.lock
git commit -m "add flake.lock"

Cette commande télécharge les index de nixpkgs et disko, puis génère flake.lock avec les commits exacts. C’est ce fichier qui garantit la reproductibilité.

Disko prend en charge le partitionnement, le formatage et le montage en une seule commande :

Fenêtre de terminal
nix run github:nix-community/disko -- \
--mode disko /tmp/nixos-config/disko-config.nix

Vérification :

Fenêtre de terminal
lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT /dev/vda
# NAME FSTYPE SIZE MOUNTPOINT
# vda 20G
# ├─vda1 vfat 512M /mnt/boot
# └─vda2 ext4 19,5G /mnt

Si les partitions sont montées sous /mnt, tout est prêt.

Fenêtre de terminal
# Copier la configuration dans le futur système
mkdir -p /mnt/etc/nixos
cp -r /tmp/nixos-config/* /mnt/etc/nixos/
# Lancer l'installation
nixos-install --flake /mnt/etc/nixos#nixos-lab --no-root-passwd

L’installation télécharge les paquets depuis le cache binaire (environ 2,5 Go), construit la configuration finale et installe le bootloader. Le message “installation finished!” confirme le succès.

--no-root-passwd est utilisé ici car notre configuration n’autorise que l’accès SSH par clé — il n’y a pas de mot de passe root à définir.

Avant de redémarrer, retirez l’ISO pour que la VM démarre sur le disque dur :

Fenêtre de terminal
# Sur l'hôte : éjecter le CD-ROM
virsh change-media nixos-lab sda --eject
# Redémarrer la VM
virsh reboot nixos-lab

Attendez environ 30 secondes que le système démarre, puis connectez-vous par clé SSH (plus de mot de passe) :

Fenêtre de terminal
ssh lab@192.168.122.x
  1. Vérifier la version NixOS

    Fenêtre de terminal
    nixos-version
    # 25.11.20260413.7e495b7 (Xantusia)
  2. Vérifier le noyau et le hostname

    Fenêtre de terminal
    uname -r && hostname
    # 6.12.81
    # nixos-lab
  3. Vérifier le partitionnement

    Fenêtre de terminal
    lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT /dev/vda
    # NAME FSTYPE SIZE MOUNTPOINT
    # vda 20G
    # ├─vda1 vfat 512M /boot
    # └─vda2 ext4 19,5G /
  4. Vérifier l’espace disque

    Fenêtre de terminal
    df -h / /boot
    # /dev/vda2 20G 2,7G 16G 15% /
    # /dev/vda1 511M 36M 476M 7% /boot
  5. Vérifier SSH et la sécurité

    Fenêtre de terminal
    systemctl is-active sshd
    # active
    grep -E "^(PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config
    # PasswordAuthentication no
    # PermitRootLogin prohibit-password
  6. Vérifier la configuration déclarative

    /nix/store/6sa64y...-nixos-system-nixos-lab-25.11.20260413.7e495b7
    readlink /run/current-system

    Ce lien symbolique pointe vers la génération active dans le store Nix. Chaque rebuild créera une nouvelle génération.

  7. Vérifier la locale

    Fenêtre de terminal
    echo $LANG
    # fr_FR.UTF-8

Si toutes ces vérifications passent, votre VM NixOS est opérationnelle.

La vraie puissance de NixOS apparaît quand vous modifiez la configuration. Ajoutons le serveur web nginx pour observer le mécanisme en action.

Connectez-vous à la VM et éditez la configuration :

Fenêtre de terminal
ssh lab@192.168.122.x
sudo vim /etc/nixos/configuration.nix

Ajoutez ces lignes avant le system.stateVersion :

# Serveur web nginx (test déclaratif)
services.nginx = {
enable = true;
virtualHosts."localhost" = {
root = "/var/www/html";
locations."/" = {
index = "index.html";
};
};
};
# Créer la page d'accueil
systemd.tmpfiles.rules = [
"d /var/www/html 0755 root root -"
"f /var/www/html/index.html 0644 root root - '<h1>Hello from NixOS!</h1>'"
];
Fenêtre de terminal
cd /etc/nixos
sudo git add -A && sudo git commit -m "add nginx"
sudo nixos-rebuild switch --flake .#nixos-lab

NixOS va :

  1. Lire la configuration modifiée
  2. Calculer les différences avec la génération actuelle
  3. Télécharger/construire nginx et ses dépendances
  4. Créer une nouvelle génération du système
  5. Activer cette génération (nginx démarre automatiquement)
Fenêtre de terminal
# Vérifier que nginx est actif
systemctl status nginx.service
# ● nginx.service - Nginx Web Server
# Active: active (running)
# Tester la page
curl http://localhost
# <h1>Hello from NixOS!</h1>
# Voir les générations
sudo nix-env --list-generations -p /nix/var/nix/profiles/system
# 1 2026-04-14 10:47:12
# 2 2026-04-14 11:09:05 (current)

Vous avez maintenant deux générations. La génération 1 est le système de base, la génération 2 inclut nginx. Les deux coexistent dans le store.

Chaque nixos-rebuild switch crée un point de restauration automatique. Au boot, le menu systemd-boot affiche toutes les générations disponibles. Si une modification casse quelque chose, vous pouvez redémarrer sur une génération précédente — sans snapshots, sans sauvegardes, c’est intégré.

Si nginx pose problème, vous pouvez annuler la modification immédiatement :

Fenêtre de terminal
sudo nixos-rebuild switch --rollback

Vérifiez que nginx a disparu :

Fenêtre de terminal
systemctl status nginx.service
# Unit nginx.service could not be found.

Le système est revenu à la génération 1, exactement comme avant l’ajout de nginx. Rien n’a été « désinstallé » — le système active simplement l’ancienne génération.

Pour revenir à la génération avec nginx, reconstruisez :

Fenêtre de terminal
cd /etc/nixos
sudo nixos-rebuild switch --flake .#nixos-lab

Avec le temps, le store accumule les anciennes générations. La garbage collection automatique est déjà configurée dans notre configuration.nix, mais vous pouvez nettoyer manuellement :

Fenêtre de terminal
# Supprimer les générations de plus de 7 jours
sudo nix-collect-garbage --delete-older-than 7d
SymptômeCause probableSolution
ISO : « Access Denied »Secure Boot activé sur OVMFUtiliser OVMF_CODE_4M.fd avec loader.secure=no
« No space left on device »RAM insuffisante (tmpfs)Passer la VM à 8 Go de RAM minimum
SSH « Permission denied » sur ISOMot de passe root non définiUtiliser virsh send-key et passwd
assertion copyInputToStorePas de dépôt Gitgit init && git add . && git commit avant nix flake lock
Boot : « waiting for device disk-main-root »Modules virtio absents de l’initrdAjouter imports = [ qemu-guest.nix ] dans configuration.nix
Boot : UEFI Shell au lieu du systèmeOrdre de boot incorrect (CD avant HD)Éjecter l’ISO : virsh change-media nixos-lab sda --eject
virsh send-key : lettres perduesDélai entre touches trop courtPasser à 0.3 seconde entre chaque touche
Fenêtre de terminal
# Depuis l'hôte : voir l'état de la VM
virsh list --all
virsh domifaddr nixos-lab
# Depuis la VM : inspecter le disque
lsblk -o NAME,PARTLABEL,FSTYPE,SIZE,MOUNTPOINT /dev/vda
blkid /dev/vda*
# Depuis la VM : voir les modules chargés
lsmod | grep virtio
# Depuis la VM : voir les générations
sudo nix-env --list-generations -p /nix/var/nix/profiles/system

À ce stade, vous avez accompli les étapes fondamentales :

  1. Installation réussie : NixOS boot en UEFI sur une VM KVM, SSH par clé fonctionnel
  2. Configuration déclarative comprise : vous savez à quoi servent flake.nix, configuration.nix et disko-config.nix
  3. Première machine reproductible : les fichiers de configuration suffisent à recréer le système à l’identique
  4. Modification déclarative réussie : ajout de nginx → rebuild → validation
  5. Rollback testé : retour à la génération précédente en une commande

Ce premier lab pose les bases. Les sujets suivants seront couverts dans les prochains épisodes :

  • Autres méthodes d’installation : nixos-anywhere pour installer à distance via kexec
  • Réparation avancée : diagnostiquer et réparer un NixOS qui ne boot plus
  • Gestion des secrets : intégrer des mots de passe, clés API et certificats de manière sécurisée
  • Durcissement : appliquer les recommandations ANSSI BP-28 à un poste NixOS
  • Secure Boot : activer le Secure Boot avec des clés personnalisées
  • Génération d’images : produire des images QCOW2, RAW ou AMI directement depuis la configuration
  1. NixOS se configure entièrement par des fichiers : flake.nix pour les dépendances, configuration.nix pour le système, disko-config.nix pour le disque
  2. Les flakes verrouillent les versions : flake.lock garantit que deux installations identiques produisent le même système
  3. Disko remplace fdisk/parted : le partitionnement est déclaratif, reproductible et versionné
  4. Chaque rebuild crée une génération : vous pouvez revenir en arrière instantanément avec nixos-rebuild switch --rollback
  5. Le profil qemu-guest.nix est indispensable pour une VM KVM sans hardware-configuration.nix — il charge les modules virtio dans l’initrd
  6. 8 Go de RAM minimum pour la VM, sinon l’évaluation Nix sature le tmpfs
  7. Git est obligatoire pour les flakes : initialisez un dépôt avant toute opération nix flake
  8. SSH par clé, pas de mot de passe : c’est la configuration par défaut de ce lab, plus sécurisée et automatisable

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn