Pourquoi un homelab ?

Un homelab, c’est un environnement informatique personnel — quelques machines (ou une seule) qu’on utilise pour apprendre, expérimenter et héberger ses propres services. Plutôt que de payer un abonnement cloud pour chaque service, on fait tourner le tout chez soi.

Mon objectif initial était simple : reprendre le contrôle de mes données et apprendre l’administration système par la pratique. Un an plus tard, le homelab fait tourner plus de 25 services en production — photos, domotique, VPN, observabilité, SSO, gestion de médias, et même de l’IA générative.

Ce que j’y ai gagné, au-delà de l’hébergement :

  • Une compréhension concrète du networking (DNS, reverse proxy, NAT, VPN mesh)
  • De l’expérience en production réelle (monitoring, alerting, incident response)
  • Une indépendance vis-à-vis des services cloud grand public

Le matériel

Pas besoin de dépenser une fortune. Mon setup actuel tient principalement sur un seul serveur physique :

  • Mini PC avec 32 Go de RAM, SSD NVMe de 1 To, processeur multi-cœur (8 threads)
  • Carte GPU NVIDIA RTX 2060 6 Go (passthrough PCIe pour le machine learning)
  • NAS Synology pour le stockage de masse (photos, sauvegardes)
  • Switch manageable pour la segmentation réseau

Le ratio qualité/prix des mini PC est imbattable pour un homelab. Ils sont silencieux, consomment peu (30-50W au repos), et ont assez de puissance pour faire tourner une vingtaine de conteneurs. L’ajout d’un GPU est optionnel — il sert uniquement pour l’accélération ML d’Immich (reconnaissance faciale et recherche sémantique de photos).

Consommation et bruit

Un point souvent négligé : le homelab tourne 24/7, donc la consommation électrique compte. Mon serveur consomme environ 40W au repos et monte à 120W sous charge GPU. Sur un an, ça représente ~350 kWh, soit environ 60€/an — bien moins que les équivalents cloud.

Côté bruit, un mini PC avec un SSD est quasi silencieux. Même avec le NAS, l’ensemble reste discret dans un placard réseau.

Proxmox VE : l’hyperviseur

Proxmox VE est un hyperviseur open-source basé sur Debian. Il supporte à la fois les VMs KVM et les conteneurs LXC, ce qui donne une flexibilité énorme.

Pourquoi Proxmox plutôt qu’ESXi ?

Le choix de Proxmox s’est imposé assez naturellement :

  • Gratuit et open-source — pas de licence à payer (VMware/ESXi impose désormais des licences même pour les homelabs)
  • Basé sur Debian — on se sent à la maison si on connaît Linux, et on peut installer des paquets système classiques
  • API REST complète — tout est scriptable, ce qui permet d’automatiser la création de VMs/LXCs
  • Support ZFS natif — snapshots instantanés, compression, envoi incrémentiel vers un backup distant
  • Interface web complète — console VNC/SPICE, monitoring, gestion du stockage, tout sans client lourd

L’alternative ESXi est devenue moins attractive depuis le rachat de VMware par Broadcom. La version gratuite a été supprimée, et même les licences “Essentials” sont devenues prohibitives pour un usage personnel.

VMs vs LXCs : quand utiliser quoi ?

Proxmox offre deux types de virtualisation, et le choix a un impact réel sur les performances :

CritèreLXCVM KVM
OverheadQuasi nul (partage le kernel)~5-10% (émulation matérielle)
RAMPartagée dynamiquementAllouée fixe
Démarrage1-2 secondes15-30 secondes
IsolationNiveau conteneur (namespaces)Niveau matériel (complète)
Usage idéalServices mono-application (DNS, BDD)Docker, GPU passthrough, noyaux custom

Ma règle : LXC pour les services simples et mono-application (un processus principal, pas de Docker imbriqué), VM pour tout ce qui a besoin de Docker ou d’un accès matériel spécifique.

Organisation des VMs et LXCs

J’ai adopté une convention d’IDs qui facilite l’identification rapide :

IDTypeHostnameRôleIP
100VMhermesAgent IA DevOps192.168.1.35
101LXCadguardDNS (AdGuard Home + Unbound)192.168.1.54
102LXCpostgresPostgreSQL 17192.168.1.55
103LXCheadscaleVPN mesh (Headscale)192.168.1.139
104LXCredisRedis 8192.168.1.77
105LXCshiny-huntApplication Laravel192.168.1.56
106LXCgarageStockage objet S3 (Garage)192.168.1.199
107LXCkopiaBackup server (Kopia)192.168.1.154

Puis deux VMs pour Docker :

IDTypeHostnameRôleIP
VMdocker-infraVM Docker principale (~20 stacks)192.168.1.52
VMgpu-infraVM GPU (ML Immich, RTX 2060)192.168.1.195

Chaque machine a une IP fixe sur le réseau 192.168.1.0/24 et un hostname qui décrit sa fonction. Les LXCs sont tous en Debian 13 (Trixie), homogène avec le Proxmox lui-même.

LXC unprivileged + nesting

Tous les LXCs sont créés en mode unprivileged avec le flag nesting activé :

pct create 101 local:vztmpl/debian-13-standard_13.3-1_amd64.tar.zst \
  --hostname adguard \
  --unprivileged 1 \
  --features nesting=1 \
  --cores 2 \
  --memory 1024 \
  --net0 name=eth0,bridge=vmbr0,ip=192.168.1.54/24,gw=192.168.1.1

Le mode unprivileged est un choix de sécurité : le root à l’intérieur du LXC est mappé sur un UID non-privilégié côté hôte. Même si un attaquant s’échappe du conteneur, il n’a aucun privilège sur l’hyperviseur.

Le flag nesting=1 est nécessaire uniquement pour les LXCs qui font tourner Docker à l’intérieur (comme le LXC shiny-hunt qui héberge une stack Laravel Dockerisée).

Docker : l’orchestration des services

Sur la VM docker-infra, Docker Compose gère tous les services applicatifs. Chaque stack a son propre répertoire avec un compose.yml autonome :

/opt/docker/
├── traefik-stack/          # Reverse proxy + CrowdSec bouncer
├── authentik-stack/        # SSO / Identity Provider
├── observability-stack/    # Grafana + Loki + Prometheus + Promtail
├── immich-stack/           # Photos (serveur, pas le ML)
├── n8n-stack/              # Workflows automation
├── media-stack/            # Radarr, Sonarr, qBittorrent (VPN)
├── meshcentral-stack/      # Remote management
├── openwebui-stack/        # Interface LLM
├── discord-bot-stack/      # Bot Discord custom
├── www-stack/              # Blog technique (ce site !)
├── update-collector-stack/ # Centralisation des MAJ
└── ...

Le réseau Docker

Un point architectural important : tous les services exposés publiquement partagent un réseau Docker externe nommé proxy :

docker network create proxy --subnet 172.18.0.0/16

Chaque service a une IP fixe sur ce réseau, ce qui permet des références stables dans les configurations de Traefik, CrowdSec, et les healthchecks :

172.18.0.2   → traefik
172.18.0.10  → authentik-server
172.18.0.20  → grafana
172.18.0.22  → prometheus
172.18.0.30  → immich-server
172.18.0.100 → crowdsec (LAPI)

Pourquoi des IPs fixes plutôt que la résolution DNS Docker ? Parce que le split-horizon DNS avec AdGuard (wildcard *.doussot.ovh → 192.168.1.52) peut interférer avec la résolution interne Docker. Avec des IPs fixes, zéro ambiguïté.

Patterns communs à toutes les stacks

Au fil du temps, des patterns se sont imposés sur toutes les stacks :

1. Logging standardisé — chaque conteneur limite ses logs locaux :

logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

30 Mo max par conteneur. Le vrai stockage de logs est délégué à Loki via Promtail.

2. Healthchecks systématiques — chaque service critique a un healthcheck Docker :

healthcheck:
  test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/health"]
  interval: 30s
  timeout: 10s
  retries: 3

3. Wait-for-services — les services qui dépendent de PostgreSQL ou Redis utilisent un script d’attente au démarrage pour éviter les crashloops :

entrypoint: ["/scripts/wait-for-services.sh"]

Ce script tente une connexion toutes les 5 secondes pendant 5 minutes max. Sans ça, au reboot du serveur, les services démarrent avant que PostgreSQL ne soit prêt et crashent en boucle.

4. Limites mémoire — pour éviter qu’un service affamé en RAM ne tue les autres :

mem_limit: 1g
memswap_limit: 1.5g

La règle d’or : ne jamais builder sur le serveur

C’est une leçon apprise à la dure. Un docker build avec des dépendances Python (pip install numpy, pytorch…) peut consommer 8+ Go de RAM temporairement. Sur un serveur avec 32 Go partagés entre 20+ conteneurs, ça déclenche l’OOM Killer du kernel, qui tue des processus pour libérer de la mémoire. Et il tue souvent les processus les plus gros — c’est-à-dire vos VMs KVM.

J’ai eu le cas avec un build d’un bot Discord (simple app Python avec quelques dépendances). L’OOM Killer a tué la VM GPU, et avec elle le service ML d’Immich. Le load average de Proxmox est monté à 20+ sur 8 cœurs. Tout le homelab était inaccessible pendant 10 minutes.

Le workflow correct — build local, transfert de l’image :

# 1. Build en local (devcontainer ou Docker Desktop)
docker build -t mon-image:v1 .

# 2. Export
docker save mon-image:v1 | gzip > /tmp/mon-image.tar.gz

# 3. Transfert + load sur le serveur
scp /tmp/mon-image.tar.gz serveur:/tmp/
ssh serveur "gunzip -c /tmp/mon-image.tar.gz | docker load"

# 4. Redémarrage (sans rebuild !)
ssh serveur "cd /opt/docker/ma-stack && docker compose up -d"

Pour les images publiques (Grafana, Traefik, etc.), un simple docker compose pull && docker compose up -d suffit — pas de build nécessaire.

Le réseau

Le réseau est la colonne vertébrale du homelab. Mon architecture repose sur quatre briques :

DNS : AdGuard Home + Unbound

Un LXC dédié fait tourner AdGuard Home (filtrage pub + réécritures DNS) devant Unbound (résolveur récursif avec validation DNSSEC). Tous les clients du LAN utilisent ce DNS.

Le split-horizon est géré par une seule réécriture wildcard dans AdGuard : *.doussot.ovh → 192.168.1.52. Depuis le LAN, tous les sous-domaines pointent vers Traefik en local. Depuis Internet, le DNS public OVH pointe vers l’IP publique.

Reverse proxy : Traefik v3

Traefik est le point d’entrée unique pour tous les services HTTP/HTTPS. La découverte automatique via les labels Docker évite toute configuration manuelle : au docker compose up, Traefik détecte le nouveau conteneur et génère un certificat Let’s Encrypt.

VPN mesh : Headscale

Un VPN mesh WireGuard auto-hébergé avec Headscale (implémentation open-source du serveur Tailscale). Permet d’accéder au homelab depuis n’importe où — téléphone, laptop, VPS cloud — avec un simple tailscale up.

Sécurité : CrowdSec

Un IPS collaboratif intégré directement dans Traefik. Il analyse les logs d’accès, détecte les comportements malveillants, et bloque les IPs. Le ban est exponentiel : 4h au premier strike, puis 8h, 16h, 32h… jusqu’à 7 jours.

Externaliser les bases de données

Un choix architectural qui a bien vieilli : PostgreSQL et Redis sont dans des LXCs dédiés, pas dans des conteneurs Docker aux côtés des applications.

Avantages :

  • Performance — pas d’overhead Docker pour les I/O critiques
  • Backup indépendant — on peut snapshotter le LXC PostgreSQL sans toucher aux applications
  • Partage — une seule instance PostgreSQL sert Authentik, Grafana, Immich, n8n, MeshCentral, CrowdSec, et les autres
  • Upgrades simplifiées — on met à jour PostgreSQL une seule fois, pas dans chaque stack Docker

Le LXC PostgreSQL (102) tourne PostgreSQL 17. Toutes les bases sont en UTF-8 — un point qui semble anodin mais qui m’a coûté des heures de debug quand une base était en SQL_ASCII et que des noms de villes françaises (Rezé, Château-Thierry…) causaient des erreurs.

Infrastructure cloud

Le homelab ne se limite pas au serveur physique. Deux VPS gratuits complètent l’architecture :

  • Oracle Cloud (ARM64 Ampere, Paris) — héberge le serveur DERP privé pour le VPN et un agent DevOps
  • Google Cloud (e2-micro, US) — exit-node Tailscale pour obtenir une IP américaine quand nécessaire

Ces VPS sont connectés au homelab via le VPN mesh Headscale. Le trafic est chiffré de bout en bout avec WireGuard.

Monitoring et observabilité

Dès le début, j’ai mis en place une stack d’observabilité complète :

  • Prometheus pour les métriques (CPU, RAM, disque, réseau, plus les métriques applicatives)
  • Loki pour la centralisation des logs (14 jours de rétention)
  • Grafana pour la visualisation et les alertes
  • Node Exporter sur chaque machine (7 hosts monitorés)

Les alertes sont envoyées sur Discord : disque plein, CPU saturé, host down, CVE critique détectée, pic d’attaques CrowdSec…

Conseils après un an de production

  1. Commencer petit — un seul serveur, Proxmox, un LXC pour Docker, trois services. Ajouter au fur et à mesure
  2. Documenter tout dès le premier jour — les IPs, les mots de passe, les choix techniques. Mon repo de documentation MkDocs est aussi important que les configs
  3. Monitorer dès le départ — pas après le premier incident. Prometheus + Grafana se mettent en place en une heure
  4. Externaliser les BDD — PostgreSQL et Redis en LXC dédié, pas en conteneur Docker par stack
  5. Automatiser les backups — Kopia vers le NAS Synology + snapshots ZFS sur Proxmox
  6. Ne pas viser la perfection — un homelab est un terrain de jeu. L’objectif c’est d’apprendre, pas d’avoir un uptime de 99.99%
  7. Gérer les secrets proprement — un gestionnaire de mots de passe (Bitwarden) pour les secrets, jamais de mots de passe dans les dépôts Git

Le homelab est un voyage, pas une destination. Chaque problème résolu est une compétence acquise, et chaque incident est une occasion d’améliorer l’architecture.