Aller au contenu

Mon homelab : infrastructure self-hosted, d'Arch Linux à NixOS

SUKIYAKI
Date de mise à jour:

Mise à jour de septembre 2026 : depuis avril, deux machines sur trois sont passées sous NixOS. J’ai gardé le texte d’origine et ajouté ce qui a changé. Le détail est dans « Le grand chantier », plus bas.

Pourquoi self-host ?

J’ai jamais été à l’aise avec l’idée de tout confier à Google ou à un cloud quelconque. Mes données, mes services, mes configs, je préfère que ça reste chez moi. Au-delà du contrôle, monter un homelab c’est surtout un prétexte pour apprendre. Le networking, Docker, Linux en profondeur, les firewalls. Ce genre de truc, on le comprend vraiment qu’en se cassant les dents dessus. Et puis il y a le plaisir pur de bidouiller un truc qui marche exactement comme on veut.

Le hardware

Rien d’extravagant. Un Dell Optiplex 3050 récupéré pour pas cher (i3-7100T, 16 Go de RAM). C’est silencieux, ça consomme peu, et ça tourne 24/7 sans broncher. Le truc parfait pour un serveur maison. Dans le dépôt de config, cette machine s’appelle optiflex.

Depuis, une deuxième s’est ajoutée, un HP MicroServer Gen8 qui sert de NAS, gen8 de son petit nom. Quatre baies SAS branchées sur un HBA 9211-8i en mode IT, trois disques dedans montés en ZFS RAIDZ1. Le pool s’appelle tank, créé début juillet 2026. Le système tourne sur un petit SSD Kingston glissé dans la baie optique, et la machine est à 16 Go de RAM depuis l’upgrade de juillet. Le gros du stockage a déménagé là-bas. L’Optiplex monte la médiathèque par-dessus le réseau local en NFS.

L’architecture réseau

Le cœur du setup c’est Headscale, une implémentation open source du control plane de Tailscale. Ça me permet d’avoir un mesh VPN entre tous mes appareils (desktop, laptop, téléphone, tablette) sans exposer quoi que ce soit sur internet. J’ai choisi Headscale plutôt que Tailscale parce que je voulais garder le contrôle du control plane, sans dépendance à un service tiers.

Headscale tourne dans un conteneur sur l’Optiplex. Autrement dit, le control plane du tailnet tombe en même temps que cette machine. C’est un détail qui va compter plus loin.

Le serveur fait office d’exit node pour tout le réseau. Le DNS est géré par Technitium, ce qui me donne un contrôle total sur la résolution de noms et le filtrage. Pour le firewall, j’utilise nftables avec des restrictions par subnet pour l’accès SSH. Le site sukiyaki.fr tourne en IPv6 natif, configuré via systemd-networkd avec une IPv6 statique et les règles firewall de la BBox.

# systemd-networkd, IPv6 statique
# (adresse réelle remplacée par le préfixe de documentation 2001:db8::/32)
[Network]
Address=2001:db8:1234:5678::1/64
Gateway=fe80::1
DNS=2001:db8:1234:5678::1

Les services

Une vingtaine de conteneurs Docker tournent en permanence. Voici les principaux.

Authentik : SSO / Identity Provider

Authentik est mon provider d’identité centralisé. Tous les services qui le supportent s’authentifient via OIDC. Ça inclut Headscale lui-même. Quand je connecte un nouvel appareil au VPN, l’auth passe par Authentik. Un seul compte, un seul mot de passe, des permissions gérées au même endroit. C’est le genre de truc qui paraît overkill pour un homelab, mais une fois en place c’est tellement plus propre.

Forgejo : Git self-hosted

Forgejo plutôt que Gitea ou GitLab, parce que c’est léger, communautaire, et ça fait exactement ce dont j’ai besoin. J’y héberge tous mes repos privés et le CI/CD de ce site tourne via Forgejo Actions, compatible avec le format GitHub Actions, donc pas de syntaxe à réapprendre.

Jellyfin : Media server

Jellyfin pour le streaming media, couplé avec Samba pour l’accès local au NAS. Ça remplace tout besoin de Plex ou Netflix pour la médiathèque perso. Accessible depuis n’importe quel appareil sur le VPN.

Firefly III : Finances perso

Firefly III pour le suivi de mes finances, avec une synchronisation automatique via GoCardless pour récupérer les transactions de ma banque (BoursoBank). C’est pas parfait (la sync a parfois du retard), mais ça me donne une vision claire de où part mon argent sans confier mes données bancaires à une app tierce.

Ollama : Inférence IA à distance

Le setup dont je suis le plus fier. Ollama tourne sur ma machine desktop sous CachyOS équipée d’une RX 7900 XTX, accessible à distance via Headscale. La machine est éteinte par défaut. Quand j’ai besoin d’inférence, je l’allume via Wake-on-LAN avec UpSnap, j’utilise le modèle qu’il me faut, et elle se ré-éteint. Toute la puissance d’un GPU récent sans qu’il tourne 24/7.

Les autres

Sécurité

La première règle c’est que rien n’est exposé sur internet qui n’a pas besoin de l’être. Headscale fait que tous les services sont accessibles uniquement via le VPN. Le disque est chiffré avec LUKS. Le boot est sécurisé via rEFInd avec Secure Boot et des clés custom, pas les clés Microsoft par défaut. nftables restreint l’accès SSH à certains subnets seulement. Et Authentik centralise l’authentification avec du OIDC partout où c’est possible.

Le grand chantier : passer en déclaratif avec NixOS

Pourquoi

Le problème d’une machine qu’on administre à la main, c’est qu’au bout d’un moment plus personne ne sait vraiment ce qu’il y a dessus. Une règle de firewall ajoutée un soir. Un fichier dans /etc bidouillé pour débloquer un truc. Un service activé et oublié. Six mois plus tard la machine tourne, mais la doc ment.

NixOS répond à ça de façon assez radicale. La configuration du système est un fichier, et le système n’est rien d’autre que le résultat de ce fichier. Si ce n’est pas écrit dedans, ça n’existe plus au prochain rebuild. L’historique de l’infra devient un git log, et quand une mise à jour casse quelque chose on redémarre sur la génération d’avant.

D’où un dépôt nix-dotfiles : 39 commits entre le 5 juillet et le 17 août 2026, trois machines dedans.

Une précision quand même, parce qu’elle revient tout le temps dans la suite. NixOS ne remplace pas Docker ici. Il décrit la couche système (disques, réseau, utilisateurs, firewall, unités systemd, sauvegardes). Les stacks compose, elles, restent des données, pas de la config Nix.

Un flake, trois machines

Ce qui est commun est factorisé dans des modules partagés que les trois importent : les réglages Nix et le kit CLI de base, la locale (interface en anglais, formats français, Europe/Paris), et le client Tailscale déjà pointé sur mon Headscale.

# hosts/gen8/default.nix
imports = [
  ./hardware.nix
  ../../modules/nixos/base.nix
  ../../modules/nixos/locale.nix
  ../../modules/nixos/tailscale.nix
];

Le découpage disque passe par disko, donc le partitionnement est déclaratif lui aussi, et l’environnement utilisateur du portable par home-manager.

Au quotidien, ça se résume à deux commandes :

# appliquer la config
sudo nixos-rebuild switch --flake .#gen8
# revenir à la génération d'avant si ça a cassé
sudo nixos-rebuild switch --rollback

Une ISO d’installation maison

Le flake construit aussi sa propre image d’installation. Elle embarque sshd avec mes clés dès le boot, NetworkManager pour installer en wifi, disko, et une copie du flake. Une seule ISO, n’importe laquelle des machines, y compris celle qu’on est justement en train de réinstaller parce qu’on l’a cassée.

Un détail qui m’a coûté une soirée. L’ISO minimale d’upstream n’active pas les flakes. Tant qu’on n’a pas ajouté l’option, disko et nixos-install --flake échouent tous les deux dessus.

gen8 : le NAS, en production

C’est la première machine passée sous NixOS, début juillet, et c’était le bon terrain d’essai, parce que rien de critique ne dépendait encore d’elle.

Tout ce qui fait un NAS y est déclaré. Le pool ZFS et son scrub mensuel. Les auto-tests SMART, décalés exprès pour ne jamais tomber en même temps que le scrub. Les snapshots sanoid sur le dataset qui contient les données Nextcloud, pas sur la médiathèque, qui est du contenu re-téléchargeable et ne mérite pas qu’on retienne ses blocs. La base Postgres de Nextcloud est dumpée chaque nuit à 03h30 sur le pool, juste avant le snapshot de 04h00, parce qu’elle vit sur le SSD système que ZFS ne protège pas. Et Samba partage l’ensemble, pendant qu’un export NFS réservé à l’Optiplex sert les binds Docker de Jellyfin et compagnie.

Le tout envoie des mails : SMART, erreurs ZFS, résultats de scrub. J’ai laissé le mode verbeux exprès. Le mail de fin de scrub mensuel sert de battement de cœur. Un système qui ne dit jamais rien, on ne sait pas s’il va bien ou s’il est devenu muet.

nixpad : le portable

Le T480s tourne sous Libreboot, donc pas d’UEFI. BIOS legacy, et le GRUB du firmware qui chaîne le GRUB du disque. disko s’occupe du GPT, de la partition EF02 de 1 Mio que ça impose, et du LUKS. Contrainte amusante : la passphrase doit être tapable en QWERTY US, parce que le prompt GRUB ne charge aucun keymap custom.

Le lecteur d’empreintes a été le plus gros morceau. C’est un capteur Validity que libfprint ne gère pas en amont. Il faut passer par un flake communautaire, épinglé sur sa propre version stable parce que c’est la seule combinaison que son auteur teste. Deux backends : un premier pour l’amorçage, qui apparie le capteur, y téléverse le firmware et produit les données de calibration, puis un second qui reprend ces données et permet enfin d’utiliser un fprintd standard, donc PAM qui marche sans bricolage.

Libreboot offre aussi un bonus inattendu. Les BIOS constructeurs verrouillent le MSR d’undervolt depuis Plundervolt, pas lui. En échange, le module thinkpad_acpi ne se charge plus tout seul et il faut le forcer pour récupérer le contrôle du ventilateur et les seuils de charge de la batterie.

optiflex : la machine qui n’a pas encore bougé

Et c’est là que l’histoire devient moins propre.

La config d’optiflex est écrite. Elle date du 11 août, c’est un portage fidèle de la machine Arch relevée ligne par ligne : réseau, utilisateurs avec uid et gid figés pour que les fichiers restaurés retombent sur leurs pieds, montages, Samba, SMART, mail sortant, réglages du démon Docker, unités de sauvegarde. L’IPv6 publique, qui était un service systemd écrit à la main, y tient en trois lignes déclaratives. Elle évalue. Elle n’a jamais démarré. La machine tourne toujours sous Arch.

Deux raisons à ça.

La première, c’est que cette machine est le homelab. Headscale y tourne, donc mon accès distant tombe avec elle. Authentik aussi, le reverse proxy qui tient le 80/443 aussi, le DNS, le relais mail, Forgejo. On ne réinstalle pas ça un dimanche soir pour voir.

La seconde, c’est la distinction config / données. NixOS reconstruit la couche système en une commande, mais il ne recrée pas /opt/stacks avec ses fichiers compose et ses .env, ni les volumes Docker nommés (il y en a plus de quatre-vingts), ni les certificats du reverse proxy, ni l’état de Tailscale, ni l’ACL Headscale. Tout ça, c’est de la restauration. Et une restauration, ça se prépare.

Donc c’est étalé, dans cet ordre :

  1. Les sauvegardes d’abord. Depuis mi-août, l’Optiplex pousse chaque nuit à 03h15 son état irremplaçable vers le NAS : Vaultwarden, Headscale, la config et les certificats du proxy, Forgejo, le compose et les .env de chaque stack. La clé côté NAS est enfermée dans un rrsync. Elle peut écrire dans ce dossier-là et strictement rien d’autre. Le versionnement, ce sont les snapshots ZFS de gen8, et les restaurations sont des fichiers normaux. Si le job échoue, je reçois un mail, envoyé en curl et pas par sendmail. Un mail qui dit « Docker est cassé » ne peut pas partir par un relais qui tourne dans Docker.
  2. L’installation sur un second disque. NixOS ira sur le M.2, avec sa propre partition EFI. Le SSD Arch reste intact à côté. C’est l’ordre de démarrage du firmware qui décide lequel des deux systèmes monte, donc le retour en arrière coûte un reboot.
  3. La remontée des stacks, dans l’ordre : DNS, puis reverse proxy, puis Headscale, puis relais mail, puis Authentik, puis le reste. Avec les pièges qui vont avec. Deux réseaux Docker externes à créer à la main avant de démarrer quoi que ce soit, un troisième surtout pas, parce que c’est le compose qui le possède.

Deux machines sur trois, donc. Et il reste celle qui compte.

Ce que j’ai appris

Ce homelab m’a appris plus que n’importe quel cours. Le networking en profondeur (IPv6, VPN, firewall, DNS), c’est le genre de truc qu’on comprend vraiment qu’en le configurant soi-même. Pareil pour Docker. Gérer une vingtaine de conteneurs en conditions réelles, avec du vrai networking entre eux, c’est un autre monde que le tuto « hello-world ». J’ai aussi appris à documenter mes configs, parce que dans six mois tu te souviens plus pourquoi t’as mis telle règle nftables. Et surtout, j’ai debug des vrais problèmes : ROCm qui refuse de voir le GPU, Docker networking sur Arch qui se comporte pas comme sur Ubuntu, des trucs que Google ne résout pas en deux clics.

Le passage à NixOS a rajouté sa propre couche de leçons.

La plus utile n’a d’ailleurs rien à voir avec Nix. C’est le relevé. Pour porter la machine Arch, il a fallu la lire ligne par ligne, et c’est là qu’on retrouve tout ce qui avait été changé en live et jamais noté nulle part. Écrire une config déclarative, c’est surtout un prétexte pour redécouvrir sa propre machine.

Deux pièges m’ont coûté cher :

Et un troisième, plus vicieux, côté Nix lui-même : les références github: d’un flake se résolvent via l’API GitHub, qui rate-limite mon IP. Quand ça arrive, nix ne dit rien et retombe silencieusement sur une révision en cache. Mes inputs sont restés bloqués sur une version de mai malgré des nix flake update répétés. Tout est passé en git+https depuis.

La suite

La priorité, c’est la bascule d’optiflex. Le reste attend derrière.

Côté monitoring, il y a maintenant un agent Beszel sur les deux serveurs, avec le hub sur le NAS. Ça donne au moins une vue d’ensemble. Un vrai Prometheus + Grafana reste sur la liste.

L’autre direction, c’est de faire de gen8 le nœud toujours allumé et de laisser l’Optiplex dormir par défaut, réveillé au besoin en Wake-on-LAN, exactement comme le desktop avec Ollama. Le Wake-on-LAN est déjà déclaré dans sa config NixOS, justement pour qu’il survive à chaque rebuild. Ça suppose d’abord d’avoir fini de déménager les services qui doivent rester joignables en permanence : le dashboard qui sert à réveiller la machine ne peut pas dépendre de la machine qu’il réveille.

Et un jour, peut-être, le MacBook via nix-darwin. Le desktop, lui, reste en dehors du dépôt : tout n’a pas besoin d’être déclaratif.

Suivant
Peti Virage : la physique des pneus, de Bevy à Godot