Installation de NixOS sur une machine dédiée OVH
Table des matières
On se propose d'installer NixOS sur une machine dédiée OVH.
La machine sera installée avec un système de fichier ZFS. Avec des datasets séparés pour /, /var, /home, etc.
ZFS apportera la prise d'instantanés (snapshots) sur plusieurs jours facilement et sans perte de performance. Récupérer un
fichier suprimé par erreur prend alors quelques secondes en accédant au répertoire spécial .zfs.
La sauvegarde sera aussi facilitée à l'avenir en envoyant les instantanés ZFS vers une autre machine.
Pour automatiser le plus possible l'installation, nous utiliseront Disko et Nixos-anywhere.
Nixos-anymwhere se chargera de créer un module de configuration hardware.nix adapté au matériel de la machine, puis l'installera
en utilisant Disko pour partitionner et formatter les systèmes de fichier.
Cet article a été écrit pour l'installation d'un serveur Kimsufi KS-5 avec deux disques SSD de 450Go, mais il devrait être adaptable facilement à d'autres modèles.
Prérequis
Cet article présuppose que vous connaissez un peu NixOS et que vous aimeriez l'installer à distance sur un serveur OVH sans jongler avec des montages de disque ISO virtuels.
Pour lancer les outils d'installation que nous allons utiliser, vous devez avoir une machine tournant sous NixOS, ou bien un environement Nix installé sur votre machine. Les "flakes" doivent être activés dans votre environnement, voir la procédure sur le site NixOS.
L'installation a été faite depuis une machine Linux NixOS avec CPU AMD. Il est possible que depuis une autre architecture comme un Mac, vous
deviez adapter le fichier flake.nix.
Personnalisation des exemples de codes donnés
Tout au long de l'article, vous pourrez entrer les valeurs correspondantes à votre situation dans des cases de formulaire. Les exemples de code de l'article utiliseront vos valeurs pour pouvoir être utilisés directement. La modification est faite en local avec Javascript, aucune donnée n'est envoyée nulle part.
Sinon vous pouvez simplement éditer les exemples dans votre logiciel préféré.
Préparation
N'effectuez les opérations décrites dans cet article que pour installer NixOS sur une machine vierge de toutes données que vous souhaiteriez garder.
Mise en place du mode "rescue" OVH
- Dans l'interface OVH, passez la machine en mode "rescue", avec authentification via votre clé SSH pour plus de comfort.
- Désactivez la surveillance OVH car NixOS ne fait pas partie des OS qu'OVH propose à l'installation, ils ne pourront pas intervenir dessus.
- Rebootez la machine en mode rescue.
- Notez son adresse DNS, vous pouvez la renseigner ci-dessous.
Vérification de la machine et obtention d'information
Une fois la machine rebootée, s'y connecter en SSH en utilisant des options pour empecher l'ajout de sa clé dans SSH (c'est celle du mode rescue, temporaire).
Noter les identifiants du ou des disques présents:
Prenez l'identifiant du ou des disques, en ignorant les partitions. Exemple:
/dev/disk/by-id/nvme-INTEL_SSDPE2MX450G7_AZERTYAZERTYAZERTY
/dev/disk/by-id/nvme-INTEL_SSDPE2MX450G7_QWERTYQWERTYQWERTY
Vous pouvez les renseigner ci-dessous.
Préparation de la machine
Cette partie est juste une préparation classique de serveur non spécifique à NixOS.
Vérifier la santé des disques:
Vous Devriez Voir que la santé est "PASSED".
Formatage des disques NVMe
N'effectuez les opérations décrites dans cet article que sur une machine vierge de toutes données que vous souhaiteriez garder.
Si votre serveur utilise des disques NVMe, vous pouvez les configurer pour une performance optimale. Les disques NVMe peuvent fonctionner suivant divers adressage de blocks (LBA, logical block addressing), chacun offre un espace de métadonnées différent (inutile ici), et une taille de secteur. Lancez la commande suivante sur chaque disque:
|
|
Vous obtenez une listes des format LBA avec leurs numéros et caractéristiques:
)
)
Les formats avec métadonnées sont inutiles pour notre besoin ; ils sont utilisés par des outils de protection de l'intégrité des données ; mais c'est inutile pour ZFS. (voir la documentation ZFS pour plus d'explications).
Dans cet exemple, le disque est livré avec un format LBA utilisant des secteurs de 512 octets et ce n'est pas optimal: Si on écrit 4Ko sur le disque, il faudra 4 écritures de 512 octets, alors que le disque pourrait en recevoir une de 4Ko directement. La documentation ZFS indique d'utiliser des secteurs de 4Ko si disponibles pour éviter de multiplier les opérations d'écriture.
Reformatez vos disques avec le LBA optimal. Exemple, ici, on sélectionne le format LBA 3
Vérification, vous devez avoir le "(in use)" devant votre format optimal.
|
|
Lors du retour de votre serveur à OVH dans le futur, vous pourriez vouloir formater votre disque NVMe en LBA 0 à 512 secteurs, tout en écrasant les données dessus. Vous pourrez utiliser ces commandes:
Puis l'écrasement des données avec:
Génération d'un hostId pour ZFS
Pendant que vous êtes sur votre machine OVH, vous pouvez générer un identifiant unique "hostId" pour ZFS. Cet identifiant doit être unique pour chacune de vos machines, afin que ZFS sache à quelle machine quel pool ZFS appartient.
L'identifiant est une chaine hexadécimale de 4 octets, vous pouvez la générer avec ces commandes sur l'environement rescue OVH:
| |
Vous pouvez renseigner l'hostId ci dessous.
Préparation d'une configuration NixOS
Taille des partitions swap
Configuration ZFS
Choisissez le nom du "pool" ZFS que vous désirez, tank est le nom le plus courant. Ensuite l'espace libre à
réserver sur vos disques, pour ne pas perdre de performance sur ZFS il faut garder 20% de l'espace non utilisé. Reste ensuite le "hostId" ZFS, un
identifiant unique hexadécimal de 4 octets pour chaque machine. Il permet à ZFS de savoir quel pool appartient à quelle machine.
Compte de l'utilisateur
Entrez le nom de l'utilisateur à créer sur la machine. Cet utilisateur aura les droits sudo sans mot de passe. Entrez aussi votre clé SSH publique (le contenu de votre fichier .ssh/id_rsa.pub) pour pouvoir vous y connecter une fois la machine installée.
Fichiers Nix
Plusieurs fichiers doivent être créés. Vous pouvez copier coller les exemples ci-dessous, ou bien télécharger l'archive avec vos valeurs personnalisées si vous avez renseigner les cases de formulaire. (L'archive est générée via Javascript, vos données ne quittent pas votre navigateur.)
-
Le fichier
flake.nix. Il importe les outils Disko, définit un environement shell avec l'outil Nixos-anywhere et la configuration de votre machine. Plus d'information sur les "flakes".flake.nix
{ description = "Ma machine nommachine chez OVH"; # Entrées utilisées. inputs = { # NixOS et ses paquets. nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; # Disko, le module de partitionnement disque. disko = { url = "github:nix-community/disko"; inputs.nixpkgs.follows = "nixpkgs"; }; }; # Productions du flake. Ici ce sera un environement shell et la configuration de la machine. outputs = { self, nixpkgs, disko, ... }: let # On récupère les paquets x86_64-linux dans nixpkgs. system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; }; in { formatter.x86_64-linux = pkgs.nixfmt-tree; # Définir un environement shell contenant l'outil nixos-anywhere pour installer la machine. devShells.x86_64-linux.default = pkgs.mkShell { packages = with pkgs; [ nixos-anywhere ]; }; # Définition de votre machine. nixosConfigurations.nommachine = nixpkgs.lib.nixosSystem { inherit system; inherit pkgs; modules = [ # Mettre a dispo les options disko dans la configuration. disko.nixosModules.disko # Fichier contenant la configuration hardware. Vide au début, il sera généré par nixos-anywhere. ./hardware.nix # Fichier décrivant le partitionnement et les systèmes de fichiers ./disk.nix # Configuration NixOS de la machine. ./configuration.nix ]; }; }; } -
Le fichier
disk.nix, il définit les partitions disque et les systèmes de fichier à installer. Renseignez les valeurs device avec celles notées sur votre machine. Si vous n'avez qu'un disque, retirer en un du fichier.disk.nix
# Arguments passés au module. Nix s'occupe de les fournir automatiquement s'ils # sont définis dans le contexte de l'appelant. { disko, pkgs, lib, ... }: { networking.hostId = "valeurHostId"; # Partitionnement des disques. Ici deux disque NVME de 450Go. disko.devices = { disk = { # Votre premier disque disk1 = { type = "disk"; device = "/dev/disk/by-id/iddisque1"; content = { # Type de table de partition. GPT pour les ordinateurs modernes. type = "gpt"; # Ici on liste les partitions et leurs types. partitions = { # On créé une partition EFI où le noyau sera placé pour pouvoir démarrer. ESP = { size = "1G"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot/efis/disk1"; mountOptions = [ "umask=0077" ]; }; }; # Partition de swap. swap = { size = "tailleswap"; content = { type = "swap"; # Effacer le swap au démarrage et lorsqu'un secteur est libéré. discardPolicy = "both"; }; }; # Le reste est alloué à ZFS. "zfs_nompool" = { size = "100%"; content = { type = "zfs"; pool = "nompool"; }; }; }; }; }; # Second disque, à retirez si vous n'en avez qu'un. disk2 = { type = "disk"; device = "/dev/disk/by-id/iddisque2"; content = { type = "gpt"; partitions = { ESP = { size = "1G"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot/efis/disk2"; mountOptions = [ "umask=0077" ]; }; }; swap = { size = "tailleswap"; content = { type = "swap"; discardPolicy = "both"; }; }; "zfs_nompool" = { size = "100%"; content = { type = "zfs"; pool = "nompool"; }; }; }; }; }; }; # Definition de la pool ZFS. zpool = { nompool = { type = "zpool"; # On créé un mirroir ZFS avec nos deux disques. Si vous n'avez qu'un disque, supprimez "mode" tout entier. mode = { topology = { type = "topology"; vdev = [ { mode = "mirror"; members = [ "/dev/disk/by-partlabel/disk-disk1-zfs_nompool" "/dev/disk/by-partlabel/disk-disk2-zfs_nompool" ]; } ]; }; }; # Options pour les volumes ZFS rootFsOptions = { acltype = "posixacl"; atime = "off"; # Ne pas écrire les date d'accès aux fichiers pour préserver la performance. compression = "zstd"; # Compression au niveau ZFS. "zstd" est un bon compromis entre compression et performance. xattr = "sa"; # Par défaut, ne pas marquer les volumes comme étant soumis à la prise d'instantanés (snapshots) automatiques. # On utilisera Sanoid pour faire des instantanés régulièrement. "com.sun:auto-snapshot" = "false"; }; # Taille des secteurs sur disque. Généralement 12 (secteurs de 4Ko). Si votre disque à des secteurs de # 512 octets, choisissez plutôt 9. options.ashift = "12"; # Définition des datasets ZFS. Vous pouvez bien entendu utiliser les conventions que vous préférez ici. datasets = { # DS local, volumes non systèmes utilisées par la machine elle même. "local" = { type = "zfs_fs"; options.mountpoint = "none"; }; # Espace non alloué de 20% de l'espace disque pour un bon fonctionnement de ZFS. "local/reserved" = { type = "zfs_fs"; options.mountpoint = "none"; options.reservation = "reserveZFS"; }; # Dataset système. "system" = { type = "zfs_fs"; options.mountpoint = "none"; }; # Racine. Montée sur /. "system/root" = { type = "zfs_fs"; mountpoint = "/"; options.mountpoint = "legacy"; }; # Un volume pour le magasin Nix. "system/nix" = { type = "zfs_fs"; mountpoint = "/nix"; options.mountpoint = "legacy"; }; "system/var" = { type = "zfs_fs"; mountpoint = "/var"; options.mountpoint = "legacy"; }; "system/var/lib" = { type = "zfs_fs"; mountpoint = "/var/lib"; options.mountpoint = "legacy"; # Compression plus rapide pour les fichiers de /var/lib qui changent souvent. options.compression = "lz4"; }; "system/var/log" = { type = "zfs_fs"; mountpoint = "/var/log"; options.mountpoint = "legacy"; }; # Des volumes pour les utilisateurs et le compte root. "user" = { type = "zfs_fs"; options.mountpoint = "none"; }; "user/home" = { type = "zfs_fs"; mountpoint = "/home"; options.mountpoint = "legacy"; }; "user/root" = { type = "zfs_fs"; mountpoint = "/root"; options.mountpoint = "legacy"; }; }; }; }; }; # Définition du boot Grub. Avec les options pour démarrer sur ZFS. boot.loader.systemd-boot.enable = false; boot.loader.grub = { enable = true; zfsSupport = true; efiSupport = true; # Indiquer à GRUB que les 2 partitions EFI doivent être peuplées à l'identique. mirroredBoots = [ { devices = [ "nodev" ]; path = "/boot/efis/disk1"; } { devices = [ "nodev" ]; path = "/boot/efis/disk2"; } ]; }; boot.loader.grub.efiInstallAsRemovable = true; boot.loader.efi.canTouchEfiVariables = false; boot.loader.efi.efiSysMountPoint = "/boot/efis/disk1"; # Nettoyage automatique de ZFS. services.zfs.autoScrub.enable = true; # Configurer ZFS pour qu'il envoie des commandes trim aux disque SSD (pour libérer les secteurs non utilisés). services.zfs.trim.enable = true; # Prise d'instantanés. Le service Sanoid effectue des instantanés suivant la politique de rétention indiquée. services.sanoid = { enable = true; interval = "hourly"; # Définir pour cette liste de dataset des instantanés comme suit: # 24 instantanés à intervals d'une heure. # 3 instantanés quotidiens. datasets = (lib.genAttrs [ "nompool/local" "nompool/machines" "nompool/services" "nompool/shared" "nompool/system" "nompool/user" ] (vol: { autoprune = true; autosnap = true; daily = 3; hourly = 24; recursive = true; }) ) // { # Desactiver les instantanés pour ces DS. # Pas besoin d'instantanés ni sauvegardes du magasin Nix. "nompool/local/nix" = { autosnap = false; }; # Pas besoin de d'instantanés ni sauvegardes des logs. "nompool/system/var/log" = { autosnap = false; }; }; }; # Mettre l'outil Sanoid a disposition en ligne de commande. environment.systemPackages = with pkgs; [ sanoid ]; } -
Le fichier
configuration.nix, il définit la configuration de la machine. Dans notre exemple, on crée un utilisateur pouvant se connecter par clé SSH et ayant les droits sudo sans mot de passe. Si vous avez une configuration existante, vous pouvez l'utiliser à la place, en conservant la configuration s'occupant du boot sur ZFS.configuration.nix
# Arguments passés au module. Nix s'occupe de les fournir automatiquement s'ils # sont définis dans le contexte de l'appelant. { pkgs, lib, ... }: { # Configuration réseau simple avec DHCP networking.hostName = "nommachine"; networking.useDHCP = false; # On utilise Systemd pour configurer le réseau. systemd.network = { enable = true; networks = { # Connexion vers Internet "10-eno1" = { matchConfig.Name = "eno1"; networkConfig.DHCP = true; linkConfig.RequiredForOnline = "yes"; }; }; }; # Configuration basique pour une machine en France time.timeZone = "Europe/Paris"; i18n.defaultLocale = "fr_FR.UTF-8"; console = { font = "Lat2-Terminus16"; keyMap = lib.mkForce "fr"; useXkbConfig = true; }; # Votre utilisateur users.users.nomutilisateur = { isNormalUser = true; extraGroups = [ "wheel" ]; openssh.authorizedKeys.keys = [ "cleSSH" ]; }; # Donner les droits sudo à l'utilisateur, sans saisie de mot de passe car # vous utilisez une clé SSH pour connexion. # Attention, en production considerez l'utilisation d'un utilisateur dédié # aux déploiements NixOS, avec une clé SSH privée bien protégée sur votre # poste de travail. security.sudo.extraRules = [ { users = [ "nomutilisateur" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; } ]; # Vos paquets préférés ici environment.systemPackages = with pkgs; [ vim wget cowsay ]; # Lancement d'un service OpenSSH. services.openssh.enable = true; # Protection du SSH contre les tentatives de connexion malveillantes. services.fail2ban.enable = true; # Activation des options expérimentales Nix. Tout le monde les utilise de nos jours. nix.settings.experimental-features = "nix-command flakes"; # Suppression des choses non utilisées dans le magasin Nix régulièrement. nix.gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; # Version de NixOS utilisée pendant l'installation de la machine (même version que dans l'entrée du flake.nix). # Ne jamais changer cette valeur. system.stateVersion = "25.11"; }
Installation
L'étape suivante est le lancement de Nixos-anywhere qui peuplera le fichier hardware.nix puis installera la machine. Tout d'abord entrez
dans l'environement shell du flake où la commande nixos-anywhere est disponible:
On lance l'outil nixos-anywhere. Il va se connecter à la machine, l'examiner et générer un fichier "hardware.nix". Puis il va installer la machine.
Ne lancez la commande que pour installer une machine vierge.
Détail de la commande.
--flake .#nommachine- Déploie la machine décrite dans
nixosConfigurations.nommachine. Vous pouvez placer d'autres machines si vous le souhaiter avec des noms différents pour tout gérer avec un flake, mais attention à ne pas déployer la configuration de l'une sur une autre! Des outils de déploiement existent pour ne pas risquer une erreur entre le nom de la machine et le--target-host. --generate-hardware-config nixos-generate-config ./hardware.nix- Génère un fichier
hardware.nixcontenant les configurations adaptées au matériel de la machine. --target-host root@addrmachine- L'adresse de connexion SSH de la machine distante à installer. Nixos-anywhere va se connecter dessus, démarrer un OS Nixos dessus (avec la fonction kexec qui écrase le système en cours de fonctionnement), puis installer le système sur disque.
Sortie du mode rescue
Une fois que nixos-anywhere a terminé, il reboot la machine distante, mais vu que le mode rescue est activé, il démarrera sur le système rescue OVH.
Dans l'interface OVH, repassez votre serveur en mode lancement depuis le disque dur, puis lancez un redémarrage. Après quelques minutes vous pourrez vous connecter via SSH:
Actions post installation
N'oubliez pas de mettre un mot de passe sur le compte root, ou bien un compte ayant les droits avec Sudo. En cas de gros problème vous pourrez utiliser la fonction KVM de OVH pour vous connecter sur votre serveur en direct par mot de passe et réparrer.
Vous pouvez aussi effectuer un instantané ZFS après installation pour pouvoir y revenir si vous voulez expérimenter:
Gestion de la configuration
Une fois installé, si vous désirez faire des changements dans la configuration de la machine, vous pouvez modifier son fichier configuration.nix. Pour pousser ces changements,
on utilise la commande nixos-rebuild:
Détail de la commande.
switch- Déploie la nouvelle configuration sur la machine et configure le boot pour la lancer aussi au démarrage. Vous pouvez utiliser "test" à la place, ce qui activera la configuration sans la configurer dans le boot. C'est utile afin d'éviter de perdre la main sur une machine distante : un reboot et il redémarre sur la configuration précédente.
--flake .#nommachine- La machine distante à déployer. Cela correspond à
nixosConfigurations.nommachine. Vous pouvez en placer d'autres dans le flake si vous le souhaiter avec des noms différents, mais attention à ne pas déployer la configuration d'une machine vers le--target-hostd'une autre ! --sudo- Indique à l'outil qu'il doit utiliser sudo sur la machine distante pour obtenir les droits nécessaires au déploiement. Si vous utiliser le compte root, il n'y en a pas besoin.
--target-host nomutilisateur@addrmachine- Utilisateur et adresse de la machine distante pour que l'outil y déploie la configuration via SSH.
--build-host nomutilisateur@addrmachine- Construit la configuration sur la machine distante plutôt que de la construire sur votre ordinateur puis la téléverser vers la machine distante.
Conclusion
Vous voila avec un NixOS installé sur votre machine dédiée OVH, le tout sans avoir fait de manipulations à la main pour partitionner, formatter, ou monter un ISO d'installation NixOS.