Comment j'ai monté mon homelab : deux vieux Dell, un pfSense et un seul câble
Deux vieux serveurs Dell, un pfSense virtualisé et un seul câble vers la box : l'ordre dans lequel j'ai monté mon homelab, et les pièges qui m'ont le plus appris.
8 min de lecture#homelab #proxmox #pfsense #networking #systemd
Fin septembre, j'avais deux vieux serveurs Dell et une box internet. Trois semaines plus tard, ils font tourner ce site, le SIEM de mon projet d'études, mon assistant IA et un réseau Minecraft pour des amis. L'architecture est décrite dans la fiche du projet ; ici, je raconte dans quel ordre j'ai monté le homelab, et les pièges qui m'ont le plus appris.
Le matériel : un R630, un R320 et des disques de six ans
Le nœud principal est un PowerEdge R630 : deux Xeon E5-2630 v3, 32 threads, 64 Go de RAM au départ, 128 Go depuis le 4 octobre. Ses disques SAS 10k affichaient environ 51 600 heures de fonctionnement, soit six ans. Le second, un PowerEdge R320 à un seul Xeon, garde les sauvegardes ; ses disques système ont sept ans.
Le R320 m'a accueilli avec ce message, et refusait parfois de démarrer :
CPU MEM VTT PG Voltage is outside memory range. Reseat CPUMemtest86+ est passé sans erreur. Normal : il teste la mémoire une fois la machine démarrée, alors que l'alarme se déclenche à la mise sous tension. Restait le journal matériel de l'iDRAC, le SEL, qui ne montrait plus aucune erreur. Il était plein :
ipmitool sel info # 1024 entries, 0 bytes free: nothing new gets logged
ipmitool sel elist > sel-pve2-20260925-1427.txt
ipmitool sel clearUn journal plein sans erreur ne prouve rien. Je l'ai copié, vidé, et je le relis après chaque démarrage. Le R320 tourne depuis sur deux barrettes, 16 Go, et je n'ai toujours pas tranché entre une barrette, un emplacement et le régulateur de tension.
Le cluster avant le pare-feu
J'ai suivi un ordre précis : BIOS et iDRAC du R320 à jour, Proxmox VE sur les deux machines, un câble direct entre elles, les pools ZFS, le cluster, et le pfSense en dernier. Changer l'adresse d'un nœud déjà en cluster est pénible, alors j'ai monté le lien principal de corosync sur le câble direct, avec des adresses qui ne bougeraient plus. Pendant la bascule derrière le pare-feu, ce lien est resté intact.
La gestion des nœuds, elle, a coincé : Proxmox joint chaque nœud à l'adresse que résout son nom, et le R630 ne pouvait plus gérer le R320 (No route to host). Une fois /etc/hosts corrigé :
systemctl restart pve-cluster # it kept the old address cached
pvecm updatecerts --force # new certificate for the new addressFaute de troisième machine pour un QDevice, le R630 a deux voix sur trois : il reste quorate seul, le R320 non.
Un seul câble vers la box, aucun accès sans VPN
La box n'est branchée qu'au R630. De ce côté, le pont Proxmox n'a aucune adresse pour l'hôte : il porte le WAN du pfSense, qui tourne en VM. Le LAN du pfSense est un pont VLAN-aware, prolongé jusqu'au R320 par le câble direct, et chaque zone (services et IA, jeux, labo, TP, DMZ du site) y a son VLAN. La DMZ de la box envoie tout vers le pfSense.
Le moment délicat, c'était de retirer les nœuds du réseau de la box : après ça, sans VPN, plus aucune administration. Chaque modification de /etc/network/interfaces partait donc avec un filet, une tâche qui remet l'ancienne config au bout de trois minutes si je ne l'annule pas. Elle vit dans systemd, pas dans ma session SSH : si la connexion tombe, elle s'exécute quand même. Le principe, en simplifié :
cp /etc/network/interfaces /root/interfaces.bak # then edit the file
systemd-run --on-active=180 --unit=net-rollback \
/bin/sh -c 'cp /root/interfaces.bak /etc/network/interfaces && ifreload -a'
ifreload -a
systemctl stop net-rollback.timer # only if the session still answersDeuxième précaution : un port de secours par nœud, non câblé, à adresse fixe et sans passerelle, pour brancher un PC si le pfSense tombe. Sans le réglage ci-dessous, Linux garde la route d'un port sans câble, et le R320 aurait envoyé une partie de son trafic dans le vide :
# /etc/sysctl.d/90-linkdown.conf
net.ipv4.conf.all.ignore_routes_with_linkdown = 1
net.ipv4.conf.default.ignore_routes_with_linkdown = 1La seule exposition accidentelle est venue plus tard. Par la DMZ, le trafic d'internet arrive avec l'adresse locale de la box : des redirections réservées au réseau de la box ont donc rendu mon tableau de bord joignable depuis internet. Je les ai retirées dans la foulée, et je teste désormais chaque ouverture depuis l'extérieur.
Une sauvegarde jamais restaurée n'est qu'un espoir
Chaque nuit, la configuration des deux nœuds, /etc/pve compris, part sur le R320. Des jobs vzdump sauvegardent le pfSense chaque semaine et les conteneurs importants chaque jour, et Proxmox me prévient d'un échec par ntfy. La première sauvegarde du pfSense, je l'ai restaurée le jour même :
qmrestore /var/lib/vz/dump/vzdump-qemu-100-2026_09_25-00_31_16.vma.zst 190 \
--storage tank --unique 1Quatre secondes pour un disque de 20 Go vide à 92 %. --unique donne de nouvelles adresses MAC, mais la copie garde les adresses IP du vrai pare-feu, passerelle de tout le réseau comprise : démarrée telle quelle sur les mêmes ponts, elle les lui aurait disputées. J'ai donc débranché ses deux cartes virtuelles (link_down=1) et coupé son démarrage automatique avant de la lancer. Elle est allée jusqu'au menu du pfSense, puis je l'ai supprimée ; le vrai n'a jamais cessé de répondre.
Le vrai redémarrage
Le 4 octobre, j'ai éteint le R630 pour doubler sa RAM et ajouter une troisième paire de disques au pool ZFS. Avant, j'ai vérifié l'ordre de démarrage des invités (le pfSense d'abord, avec un délai) et que rien ne tournait lancé à la main.
Tout est revenu, sauf deux services du conteneur de l'IA. nginx avait démarré avant que le DNS réponde :
host not found in upstream "api.openai.com:443"Un petit serveur web en Python, parti avant que son adresse soit attribuée (Cannot assign requested address), a été relancé cinq fois en une fraction de seconde, puis abandonné. C'est le comportement par défaut de systemd : 100 ms entre deux relances, cinq démarrages au plus en dix secondes. L'ordre de démarrage de Proxmox garantit que le pfSense part en premier, pas que la chaîne DNS réponde déjà. Les deux services réessaient désormais toutes les cinq secondes, sans limite :
# /etc/systemd/system/nginx.service.d/redemarrer.conf
[Unit]
StartLimitIntervalSec=0
[Service]
Restart=on-failure
RestartSec=5sAujourd'hui, ce qui tourne dessus
Tout tient sur le R630 : 12 VM et 14 conteneurs, dont un modèle et un conteneur d'essai éteint, pour 105 Go de RAM occupés sur 126. Le R320 est éteint depuis fin septembre : plus rien n'en dépend, à part les sauvegardes. Par zone :
- Le socle.
fw-01, le pfSense en VM : pare-feu, NAT et VPN WireGuard.dns-01: Pi-hole et un Unbound récursif, pour le homelab comme pour la maison.mon-01: Prometheus, Grafana et Alertmanager, qui m'alerte par ntfy.homepage-01: le tableau de bord, juste en dessous. Etmodele-debian, le modèle non privilégié dont je clone chaque conteneur. - Ce site.
web-01, une VM seule dans sa DMZ, publiée par le tunnel Cloudflare. - L'IA.
hermes-01fait tourner l'agent Hermes et sert d'entrée unique à tout ce qui touche à l'IA : nginx, liste blanche, clés.llm-01héberge FreeLLMAPI, un routeur vers des modèles gratuits ;voix-01, le moteur vocal ;maj-01, les mises à jour signées de l'app HermesIAssistant. - Les jeux. Le réseau Minecraft des amis : un proxy Velocity, seule porte d'entrée, un lobby avec une boussole pour choisir son serveur, une survie vanilla, All The Mods 11 et DeceasedCraft. Cinq conteneurs.
- Le labo.
siem-01, le SIEM SOCFortress (Wazuh, Graylog, TheHive, Shuffle, Velociraptor), à lui seul 32 Go de RAM. Autour, de quoi l'alimenter : une cible volontairement vulnérable (DVWA), un client Debian, un client Windows 11 et un Active Directory sous Windows Server 2025. - Les TP. Cinq VM Windows pour le TP fil rouge de mes études : un contrôleur de domaine, un serveur de fichiers, un poste d'administration dédié et deux postes clients.
Ma pièce préférée reste la page d'accueil. Elle a commencé comme un Homepage, que j'ai stylé jusqu'à buter sur son fond gris, revenu après chaque chargement. Je l'ai remplacée par une page sur mesure, servie par nginx dans le même conteneur. Toutes les 2 secondes, un collecteur en Python lit node_exporter sur les nœuds ; toutes les 10 secondes, l'API Proxmox, avec un TLS vérifié par l'autorité du cluster ; toutes les 30 secondes, Pi-hole. Il écrit le tout dans un data.json que la page relit. Un nœud muet depuis 10 secondes passe « Hors ligne » au lieu d'afficher des valeurs figées.
En-tête du Panel, assistant Hermes et nœud principal
Plus bas, chaque zone a ses cartes. Celle de Minecraft affiche les joueurs connectés, la charge de chaque serveur et le compteur de jours de la survie, lu par RCON.
La zone Minecraft du Panel
Ce qui manque encore
Tout est dans la même pièce : pas de copie hors site, pas d'onduleur, pas de troisième voix pour le quorum. Un incendie ou une surtension, et les sauvegardes partent avec les serveurs. Et tant que le R320 reste éteint, la copie de la nuit n'a nulle part où aller. C'est le prochain chantier ; avec le recul, je l'aurais prévu avant d'avoir quelque chose à perdre.
Ce que je referais à l'identique : l'ordre, et le journal. J'administre le homelab avec Claude Code comme opérateur : il exécute en SSH, je décide et j'autorise chaque changement risqué. Chaque intervention finit dans un journal versionné avec git, 252 entrées à ce jour. C'est grâce à lui que j'ai pu écrire cet article, messages d'erreur compris. Le site, lui, a rejoint le homelab le 14 octobre, sans ouvrir un seul port.