Le LXC web-server : un blog Ghost, un CV, et un busybox qui fait le job

Un seul LXC sur le nœud le moins puissant du cluster pour héberger ce blog Ghost, un CV et un guide de voyage sur Rome, avec un grand écart entre un CMS complet avec MariaDB et un busybox httpd de 5 Mo qui fait exactement ce qu'on lui demande.

Après avoir détaillé la couche OpenTofu/Ansible qui structure mon Home Lab, entrons dans le vif du sujet avec le premier LXC applicatif : le web-server. Un seul conteneur LXC, trois sites web aux besoins très différents — et une bonne occasion d'illustrer que la bonne solution n'est pas forcément la plus sophistiquée.

Un LXC, trois sites, une même logique

Le LXC web-server héberge trois services distincts : ce blog, mon CV en ligne, et un vieux projet personnel que j'avais réalisé en classe de seconde après un voyage scolaire à Rome, un site qui retrace les visites effectuées et se veut un guide pratique pour les voyageurs qui souhaitent s'y rendre.

Ce LXC tourne sur SRV-PVE-2, le ThinkCentre M920q — le moins puissant des trois nœuds du cluster, avec son i5-9500T et ses 32 Go de RAM. Un choix délibéré : héberger des sites web, même avec un CMS comme Ghost, ne nécessite pas la puissance d'un i7 dernière génération. SRV-PVE-2 est largement suffisant pour ce cas d'usage, et ça libère les deux autres nœuds pour les charges de travail qui en ont réellement besoin — la domotique avec son GPU RTX T1000 sur SRV-PVE-3, l'infra critique sur SRV-PVE-1.

Les trois services sont orchestrés via le même playbook Ansible et le même réseau Docker interne :

- { role: web-server/www,  mode: ghost-db,     tags: www }
- { role: web-server/www,  mode: ghost-server, tags: www }
- { role: web-server/www,  mode: backup,       tags: [www, backup] }
- { role: web-server/cv,   mode: www,          tags: cv }
- { role: web-server/rome, mode: www,          tags: rome }

Les regrouper sur un même LXC plutôt qu'un LXC par site est un choix pragmatique : ils partagent la même infrastructure réseau, le même Caddy en reverse proxy en amont, et des besoins en ressources qui se complètent plutôt qu'ils ne se concurrencent.

Le blog : Ghost + MariaDB

Ce blog tourne sur Ghost, un CMS Node.js orienté publication, couplé à une base de données MariaDB. C'est le service le plus "lourd" du trio, avec une vraie pile applicative : un conteneur de base de données, un conteneur applicatif, des volumes persistants, des variables d'environnement, et une stratégie de sauvegarde dédiée.

- name: WWW => Create ghost_server container
  docker_container:
    image: "{{ container_ghost_server.image }}"  # ghost:6.42.0-alpine
    name: "{{ container_ghost_server.name }}"
    restart_policy: always
    mounts:
      - type: bind
        source: "{{ directory_path }}/www/ghost-content"
        target: /var/lib/ghost/content
    env:
      database__client: mysql
      database__connection__host: ghost_db
      database__connection__user: ghost
      database__connection__password: "{{ vault_db_password }}"
      database__connection__database: ghost
      url: https://www.jardillier.net
    ports:
      - "8090:2368"

Le contenu (articles, thèmes, images) est bind-mounté dans /opt/web-server/www/ghost-content, ce qui rend les sauvegardes triviales : un tar du répertoire plus un dump MariaDB, copiés sur le NAS via le montage NFS /mnt/backup.

Les sites statiques : busybox httpd, l'art du minimalisme

Mon CV et le site Rome ont un point commun : ce sont des sites entièrement statiques. Pas de backend, pas de base de données, pas de génération dynamique, juste des fichiers HTML, CSS et JS à servir.

Le site Rome ("Visite guidée à Rome" pour être précise), c'est un projet que j'avais réalisé au lycée après un voyage scolaire : il retrace les visites effectuées et sert de guide pour les voyageurs qui veulent se rendre dans la ville éternelle. Il date, il n'évolue pas souvent, mais il tourne, il répond, et il trouve naturellement sa place ici aux côtés du reste de l'infrastructure. C'est aussi ça un Home Lab : un endroit où les vieux projets ont le droit de continuer à exister sans prendre de place inutilement.

Pour ces deux sites, pas besoin de Nginx ou d'Apache. J'utilise busybox httpd : un micro-serveur HTTP intégré dans l'image busybox, qui pèse moins de 5 Mo et se lance en une ligne de commande.

- name: CV => Create cv container
  docker_container:
    image: "{{ container_cv.image }}"  # busybox:1.38.0
    name: "{{ container_cv.name }}"
    restart_policy: always
    volumes:
      - "{{ directory_path }}/cv/www:/www:ro"
    ports:
      - "8091:80"
    command: busybox httpd -f -p 80 -h /www

Le volume est monté en lecture seule (:ro), les fichiers du site sont déposés dans /opt/web-server/cv/www, et le conteneur se contente de les servir. C'est tout. Pour mettre à jour le site, on dépose les nouveaux fichiers dans le répertoire — Ansible n'a même pas besoin d'intervenir.

C'est une illustration directe d'un principe qui guide souvent mes choix : ne pas over-engineer. Un site statique n'a pas besoin d'un vrai serveur web. busybox httpd fait le job avec une empreinte minimale.

Ce que Caddy vient faire là-dedans

Les trois services écoutent chacun sur leur propre port côté LXC (8090 pour Ghost, 8091 pour le CV, 8092 pour Rome), mais depuis l'extérieur tout passe par Caddy, qui tourne sur le LXC infra-core et fait le routage par sous-domaine vers la bonne cible. La configuration de cette couche reverse proxy fera l'objet d'un article dédié.

Ce que ce LXC illustre

Trois sites, trois besoins, trois niveaux de complexité — et une même convention pour les déployer. Ghost méritait une vraie pile applicative. Les sites statiques méritaient la solution la plus légère possible. Et SRV-PVE-2, avec sa puissance modeste, s'acquitte parfaitement de la tâche sans mobiliser des ressources dont il n'a pas besoin.

Le LXC web-server montre que les deux peuvent cohabiter sans friction, dans la même logique de rôles Ansible, derrière le même reverse proxy — et que choisir le bon outil pour le bon besoin reste la décision d'architecture la plus importante.