ITSkillsCenter
Développement Web

WordPress multisite repensé : un conteneur Incus par client — tutoriel agence 2026

12 min de lecture

📍 Article principal du sujet : Incus 6 LTS — gérer conteneurs système et VMs Linux pour PME ouest-africaine
Pour la vue d’ensemble, lisez d’abord l’article principal qui pose le contexte d’Incus, ses concepts et son positionnement.

Pour une agence web qui héberge plusieurs dizaines de sites WordPress, deux modèles d’architecture s’opposent. Le premier consiste à empiler tous les sites dans une seule installation WordPress Multisite : économique mais fragile (un plugin défaillant casse tous les sites, une compromission expose toute la base). Le second consiste à attribuer un conteneur Incus par client, isolé en permissions, en ressources et en réseau : un peu plus de configuration initiale, mais une isolation industrielle qui change radicalement la posture sécurité, la facturation et le débogage. Ce tutoriel monte le second modèle de bout en bout, sur un VPS unique d’abord, puis discute la transition vers cluster.

L’angle business : pour une agence à Dakar, Abidjan ou Lomé qui facture l’hébergement à 5 000 ou 10 000 FCFA mensuels par client, l’isolation par conteneur représente un argument commercial concret (« votre site est sur sa propre machine virtuelle, indépendante des autres ») tout en gardant un coût d’infrastructure très contenu. Sur un Hostinger Cloud VPS 8 Go RAM, 30 sites WordPress de petite taille tournent confortablement, soit moins de 0,50 USD par site et par mois côté infrastructure — la marge brute est massive.

Pourquoi Incus plutôt que Multisite ou Docker

WordPress Multisite partage la base de données et le filesystem entre tous les sites. Quand le client A demande un plugin spécifique, on l’installe pour tous. Quand le site B est piraté, l’attaquant a un accès logique à la table wp_posts du site C. Quand un client part chez un concurrent, exporter ses données proprement demande un script de séparation non trivial. C’est un excellent modèle pour les médias d’information avec une rédaction unique, mais un mauvais modèle pour une agence qui sert des PME indépendantes.

Docker apporte l’isolation, mais pas vraiment le confort. Un site WordPress Docker se compose typiquement d’un compose à trois services (PHP, MariaDB, Nginx) ; multiplier ce schéma par 30 clients donne 90 conteneurs Docker à orchestrer, sans systemd dedans, avec des logs dispersés et des backups par compose. Réalisable mais lourd à exploiter.

Incus propose le compromis idéal : un seul conteneur système par client, contenant Apache + PHP-FPM + MariaDB + WP-CLI + cron, avec systemd qui gère tout, et la configuration WordPress identique à une installation classique. On retrouve le confort d’une VM avec le coût d’un conteneur, et l’isolation d’un Docker avec la simplicité d’une instance Linux complète.

Prérequis

  • VPS Linux 64 bits, 8 Go de RAM minimum, 80 Go SSD — Ubuntu 24.04 LTS recommandé
  • Incus 6 LTS opérationnel avec pool ZFS (compression activée par défaut)
  • Caddy installé sur l’hôte pour le reverse-proxy HTTPS automatique
  • Un nom de domaine ou des sous-domaines pointant vers l’IP publique du VPS
  • Niveau attendu : confortable avec WordPress, Linux et la notion de reverse-proxy
  • Temps estimé : 60 à 90 minutes pour un premier conteneur client complet, puis 10 minutes pour les suivants

Pour ce scénario, Hostinger Cloud VPS à 8 Go RAM (autour de 13 USD/mois) couvre une trentaine de sites WordPress de petite taille avec marge confortable. Pour un parc plus grand, deux VPS de la même formule en équilibrage manuel ou en cluster Incus offrent une bonne progression.

Étape 1 — Préparer un profil dédié WordPress

Plutôt que de configurer chaque conteneur à la main, on crée un profil wp-client qui définit les ressources et les périphériques par défaut. Toute nouvelle instance basée sur ce profil hérite de la configuration et démarre prête pour WordPress.

incus profile create wp-client
incus profile edit wp-client

Dans l’éditeur, on remplace par :

config:
  limits.cpu.allowance: 100%
  limits.memory: 768MB
  limits.memory.swap: false
  limits.processes: 500
  security.idmap.isolated: true
  security.nesting: false
description: Profil WordPress par client
devices:
  eth0:
    name: eth0
    network: incusbr0
    type: nic
  root:
    path: /
    pool: default
    size: 10GB
    type: disk
name: wp-client
used_by: []

Pourquoi ces valeurs ? 768 Mo de RAM est confortable pour un WordPress avec PHP-FPM, MariaDB, et plugins courants — la consommation effective tournera entre 200 et 400 Mo selon le trafic, mais la marge évite l’OOM en pic. 10 Go de disque est généreux : la médiathèque WordPress dépasse rarement 2-3 Go pour un site PME, le reste sert aux logs et aux backups locaux. security.idmap.isolated: true donne à chaque conteneur un mapping UID/GID unique, condition cruciale pour la sécurité multi-tenant.

Étape 2 — Créer le premier conteneur client

incus launch images:debian/12 wp-clientA -p wp-client
incus shell wp-clientA

À l’intérieur du conteneur, on installe la pile LAMP minimale et WP-CLI. La séquence est classique mais on note quelques choix optimisés pour le multi-conteneur (PHP-FPM en mode socket Unix, MariaDB sur localhost, Nginx léger en frontal).

apt update && apt upgrade -y
apt install -y nginx mariadb-server php-fpm php-mysql php-curl \
  php-gd php-mbstring php-xml php-zip php-imagick php-intl \
  curl unzip cron wget
# WP-CLI installé proprement
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
mv wp-cli.phar /usr/local/bin/wp

Cette installation pèse environ 350 Mo après mises à jour, marge confortable dans le quota 10 Go défini par le profil. Activez les services et configurez la base de données pour le client (générez un mot de passe par client avec openssl rand -base64 24).

Étape 3 — Installer WordPress dans le conteneur

cd /var/www
wp core download --locale=fr_FR --path=clientA --allow-root
cd clientA
wp config create --dbname=wp_clientA --dbuser=wp_clientA --dbpass=MOT-DE-PASSE --locale=fr_FR --allow-root
wp core install \
  --url=clientA.example.com \
  --title="Site Client A" \
  --admin_user=admin_clientA \
  --admin_password=MOT-DE-PASSE-ADMIN \
  --admin_email=admin@clientA.example.com \
  --allow-root
chown -R www-data:www-data /var/www/clientA

WP-CLI fait le gros du travail en quelques commandes : téléchargement du core en français, génération du wp-config.php avec les credentials BDD, et création du compte admin. À ce stade, WordPress est techniquement installé mais il faut encore le servir via Nginx.

Étape 4 — Configurer Nginx dans le conteneur, Caddy sur l’hôte

À l’intérieur du conteneur, Nginx écoute le port 80 sur l’IP privée. À l’extérieur, Caddy installé sur l’hôte centralise les domaines publics et applique HTTPS automatique via Let’s Encrypt. Cette séparation est clé : un seul service public expose les ports 80/443, tous les conteneurs sont sur réseau privé.

# À l'intérieur du conteneur, virtualhost Nginx :
server {
  listen 80;
  server_name _;
  root /var/www/clientA;
  index index.php;
  location / { try_files $uri $uri/ /index.php?$args; }
  location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
  }
}

Et sur l’hôte, Caddy reçoit le domaine public et fait suivre vers l’IP privée du conteneur :

# /etc/caddy/Caddyfile sur l'hôte :
clientA.example.com {
  reverse_proxy 10.124.10.42:80
  encode gzip
}
# Puis :
sudo systemctl reload caddy

Caddy demande automatiquement un certificat Let’s Encrypt pour clientA.example.com et l’applique sans configuration supplémentaire. Pour ajouter un client, c’est trois lignes dans le Caddyfile + reload.

Étape 5 — Industrialiser avec un script d’onboarding

Faire ces étapes à la main pour chaque client devient vite répétitif. Un script bash onboard-wp-client.sh orchestre l’ensemble : création du conteneur, installation, base de données, Caddy, et envoi des credentials au client.

#!/usr/bin/env bash
# onboard-wp-client.sh - provisionne un nouveau client WordPress
set -euo pipefail
CLIENT=${1:?"Usage: $0 <nom-client> <domaine> <email-admin>"}
DOMAIN=${2}
EMAIL=${3}

DB_PASS=$(openssl rand -base64 24)
ADMIN_PASS=$(openssl rand -base64 18)

# Création du conteneur
incus launch images:debian/12 wp-${CLIENT} -p wp-client

# Installation et configuration
incus exec wp-${CLIENT} -- bash -c "
  apt update && apt install -y nginx mariadb-server php-fpm php-mysql
  systemctl enable --now mariadb nginx php8.2-fpm
  mysql -e \"CREATE DATABASE wp_${CLIENT}; CREATE USER 'wp_${CLIENT}'@'localhost' IDENTIFIED BY '${DB_PASS}'; GRANT ALL ON wp_${CLIENT}.* TO 'wp_${CLIENT}'@'localhost';\"
"

# (suite : téléchargement WP, install, config Nginx, Caddy reload)
echo "Client ${CLIENT} provisionné. Domaine : ${DOMAIN}"
echo "Admin pass: ${ADMIN_PASS}"
echo "DB pass: ${DB_PASS}"

Avec ce script, l’onboarding d’un nouveau client passe à moins d’une minute en ligne de commande. La rentabilité opérationnelle d’une agence change radicalement : ce qui prenait une demi-journée par client (provisioning chez l’hébergeur, installation, certificat SSL, vérifs) se fait pendant le café.

Étape 6 — Sauvegardes par client

L’avantage de l’isolation par conteneur s’étend aux backups. Chaque conteneur Incus se snapshote indépendamment, et on peut exporter une instance entière (rootfs + base de données + config) en un seul fichier transportable.

incus snapshot create wp-clientA backup-$(date +%Y%m%d)
incus export wp-clientA /backups/wp-clientA-$(date +%Y%m%d).tar.gz \
  --snapshot=backup-$(date +%Y%m%d)

# Push vers stockage S3 distant
rclone copy /backups/wp-clientA-*.tar.gz bunny:backups-itskills/clientA/

L’archive contient absolument tout : système d’exploitation, code WordPress, base de données, médias, configuration Nginx. La restauration se fait en une commande sur n’importe quel hôte Incus, ce qui élimine totalement le scénario du vendor lock-in.

Étape 7 — Migrer un client vers cluster ou autre VPS

Quand un client devient gros et a besoin de plus de ressources, on déplace son conteneur vers un VPS dédié sans toucher au site. Sur l’hôte source :

incus remote add vps-grosClient https://vps-grosclient.example.com:8443
incus copy wp-clientA vps-grosClient:wp-clientA --refresh
incus start vps-grosClient:wp-clientA

Le DNS bascule, Caddy reconfigure, et le client n’a rien à faire. Côté agence, c’est une opération de cinq minutes au lieu d’une migration WordPress laborieuse avec WP-CLI export, import, recherche-remplace d’URLs et casse des permaliens.

Erreurs fréquentes

Erreur Cause Solution
Site WP affiche Error establishing database connection MariaDB ne tourne pas dans le conteneur incus exec wp-clientA -- systemctl status mariadb ; redémarrer si besoin
Caddy ne génère pas le certificat DNS pas encore propagé ou port 80 bloqué Vérifier la résolution DNS publique et l’ouverture des ports 80/443 côté VPS
Performance dégradée d’un site sous charge Cache HTTP non activé Plugin WP-Super-Cache ou directive cache Caddy ; en dernier recours, augmenter limits.memory
Médiathèque qui consomme tout l’espace Pas de quota disque sur le conteneur incus config device override wp-clientA root size=20GB
Plugin WP qui refuse de s’installer pour cause de droits FS_METHOD direct non défini Ajouter define('FS_METHOD', 'direct'); dans wp-config.php

Adaptation au contexte ouest-africain

L’isolation par conteneur résout un problème typique du marché ouest-africain : les agences héritent de sites clients qui ont accumulé des plugins obscurs, des modifications PHP custom, et parfois des backdoors. Sur un Multisite, ces ennuis polluent la totalité de l’installation. Sur des conteneurs séparés, un site compromis reste cantonné à son périmètre — l’attaquant n’a pas accès aux autres clients depuis ses droits dans le conteneur. C’est une assurance technique qui se vend bien : vos clients dorment tranquilles, vous aussi.

Côté facturation, la grille type d’une agence à Dakar qui passe à Incus avec ce modèle : 7 500 FCFA/mois pour l’hébergement isolé d’un site PME, 15 000 FCFA pour un site WooCommerce avec base plus chargée, 25 000 FCFA pour un site avec maintenance complète. Sur un VPS Hostinger 8 Go à environ 8 000 FCFA/mois, 30 sites en mix donnent un revenu de 250 000 à 400 000 FCFA mensuels pour un coût d’infrastructure unique de 8 000 FCFA — ratio qui justifie largement le passage à ce modèle.

Sur le support, l’avantage opérationnel est tangible : quand un client appelle parce que « mon site est cassé », on entre dans son conteneur, on regarde ses logs, on corrige son problème, sans risquer d’effet de bord sur les autres clients. Le débogage devient routinier au lieu d’angoissant.

Tutoriels frères

Pour aller plus loin

FAQ

Vaut-il mieux MariaDB par conteneur ou un MariaDB central partagé ?
Par conteneur. Le surcoût mémoire est négligeable (50-100 Mo par instance MariaDB peu sollicitée), mais l’isolation totale facilite énormément la sauvegarde et la migration par client.

Comment gérer le cron WordPress dans un conteneur ?
Désactivez WP-Cron pseudo-natif (define('DISABLE_WP_CRON', true);) et lancez un vrai cron toutes les 15 minutes via crontab -e dans le conteneur.

L’IP privée du conteneur change-t-elle à chaque démarrage ?
Pas par défaut grâce au DHCP qui réserve l’IP par MAC. Pour absolument fixer l’IP, configurez-la via le profil ou en override sur l’instance.

Comment limiter qu’un client compromis ne tape sur un autre via le réseau interne ?
ACL Incus avec règles bloquantes entre instances appliquées à incusbr0. Les conteneurs ne se voient plus mutuellement, seul l’hôte (et Caddy) y a accès.

Quel hébergeur recommander aux clients qui veulent rester maîtres ?
Pour ceux qui veulent répliquer le modèle chez eux, Hostinger Cloud VPS en 4-8 Go RAM offre le meilleur ratio prix-fonctionnalités pour la francophonie ouest-africaine, avec interface en français et paiement par carte sans surcoût.

Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité