Phase 6 — Hébergement personnel · installer votre propre Moodle

PHASE 6
← Phase 0 · Phase 1 · Phase 2 · Phase 3 · Phase 4 · Phase 5 · 📍 Phase 6 — Hébergement perso · 📋 Roadmap
🤖 100% local — gratuit — aucun crédit
SPRINT 1 · MATRICE DE DÉCISION

🧭 5 solutions pour héberger votre propre Moodle

Du Raspberry Pi dans votre salon au cluster AWS managé · 5 chemins différents selon votre budget, votre tolérance DevOps et votre besoin de scaler. Matrice de décision + verdict par profil.

🏠 5 options💰 0 € → 100 €/mois⏱ 10 min → 1 jour setup🎯 4 profils
🏠

Le besoin · votre Moodle perso, à vous, sous votre contrôle

Formation achevée · il est temps de passer à la pratique chez vous

Pourquoi héberger son propre Moodle ? · apprendre vraiment les tripes de l'outil · bâtir une démo portfolio pour votre prochain job · lancer votre side-business de formateur indépendant · monter une association de formation · offrir un espace e-learning à votre équipe · ou simplement garder la main sur vos données d'apprentissage sans dépendre d'un prestataire.

Ce que ce sprint ne fait pas · promettre que c'est gratuit. Un Moodle qui tourne 24/7 avec nom de domaine et certificat TLS coûte au minimum 4-5 € par mois. En revanche, ce sprint vous aide à éviter de sur-payer ou de choisir une solution trop complexe pour votre besoin réel.

0 €
Cost floor · Raspberry Pi
5-15 €
VPS sweet spot / mois
30 min
MoodleCloud setup
0,5 j
VPS setup bien fait
~ 1h/sem
Maintenance VPS réaliste

🎯 Les 5 options d'hébergement · de la plus simple à la plus flexible

Le + simple
☁️
MoodleCloud
Moodle HQ · SaaS officiel
Gratuit → 30 €/mois
50 users free · Starter 30 € / Pro 100 €
  • Setup en 30 min · prêt à l'emploi
  • Backups, updates, TLS inclus
  • Zéro sysadmin à faire
  • Pas de plugins tiers (!!)
  • Stockage limité (200 Mo free)
  • Branding limité gratuit
🟢 Setup facile Free tier
🥧
MoodleBox · Raspberry Pi
Open source · local
0 €/mois
+ Pi 4 à 75 € une fois
  • Distribution pré-configurée
  • Fonctionne offline · idéal zones blanches
  • Apprentissage matos pas-à-pas
  • Pas accessible depuis Internet sans VPN
  • Perfs limitées < 30 users
  • Pas d'accès externe HTTPS sans tunnel
🟢 Plug & play 0 €/mois
🐳
Docker sur portable
Moodle Docker · dev local
0 €/mois
Accès localhost uniquement
  • Idéal dev + test plugins
  • Setup 10 min avec Moodle HQ Docker
  • Démarre/stoppe à la demande
  • Pas de persistence stable
  • Non accessible aux autres
  • Pas de production
🟡 Setup dev 0 €
☸️
Cloud managé
AWS · GCP · DigitalOcean App Platform
30 € → 200+ €/mois
RDS · Elastic Beanstalk · S3 · CloudFront
  • Scale automatique sans limite
  • SLA 99,9 %+ · HA natif
  • DB managed · backups auto · CDN
  • Complexité IAM / VPC / Security Groups
  • Coût qui explose vite sans vigilance
  • Lock-in fort sur un cloud provider
🔴 Expert

⚖️ Matrice comparative · 6 critères × 5 options

Critère ☁️ MoodleCloud 🥧 MoodleBox Pi 🐳 Docker local 🖥️ VPS ☸️ Cloud managé
Setup time 30 min 1 h 10 min 0,5 j 1-2 j
Coût / mois 0-30 € 0 € 0 € ⭐ 5-15 € 30-200 €
Accessible Internet ✓ Oui ✗ Non (sauf VPN) ✗ Non ✓ Oui ✓ Oui
Users supportés 50-500 < 30 1 (dev) 200-500 Illimité
Plugins tiers ✗ Non ✓ Libre ✓ Libre ✓ Libre ✓ Libre
Maintenance requise ~ Zéro ~ Faible ~ Zéro ~ 1h/sem ~ 2h/sem

👤 Quel profil êtes-vous ? · verdict par usage

🧑‍🎓

L'apprenant curieux

Je veux comprendre Moodle de l'intérieur

Vous voulez apprendre comment Moodle fonctionne sous le capot, tester des plugins, casser/réparer sans stress. Zéro apprenant réel pour l'instant.

Verdict · 🐳 Docker local ou 🥧 MoodleBox Pi. Zéro coût · vous pouvez tout essayer. Migrez vers VPS quand vous voulez exposer publiquement.
💼

Le formateur indépendant

Je lance mon side-business formation

Vous livrez des formations payantes à 5-50 apprenants à la fois. Vous voulez votre propre plateforme avec votre branding, pas la marque Moodle HQ sous le nez.

Verdict · 🖥️ VPS Hetzner CX22 (~5 €/mois) ou OVH VPS SSD. Nom de domaine 12 €/an · TLS Let's Encrypt gratuit. Investissement 72 €/an pour une plateforme pro complète.

Le pressé sans DevOps

Je n'ai ni le temps ni l'envie de gérer un serveur

Vous voulez un Moodle qui marche, point. Pas envie d'apprendre sudo apt install, reboot kernel, certificats. Votre temps vaut plus que les 30 €/mois.

Verdict · ☁️ MoodleCloud Starter 30 €/mois. 50 apprenants inclus, backups, TLS, updates gérés. Upgrade Pro quand vous dépassez 100 apprenants.
🏢

Le consultant / PME

Je déploie pour mon client ou ma boîte

Vous opérez un Moodle pour 200-500 apprenants dans un contexte pro, avec SLA à tenir, conformité RGPD, backups fiables, scaling maîtrisé.

Verdict · 🖥️ VPS pro (Scaleway Elastic Metal ou Hetzner CCX) 15-30 €/mois OU ☸️ AWS/DigitalOcean App Platform si multi-sites. Privilégier VPS si vous apprenez encore.

💰 Coûts cachés à anticiper (souvent oubliés)

PosteEstimation / anCommentaire
Nom de domaine10-15 €.fr / .com / .academy · renouvellement annuel
Certificat TLS0 € (Let's Encrypt) ou 30-60 €Let's Encrypt = gratuit et renouvelable auto · EV obligatoire seulement si banque
Backup externe12-24 €Backblaze B2 ou Wasabi S3-compatible · 10 Go suffit pour 99 % des cas
Service email (SMTP)0 € (SendGrid free) ou 10-15 €/moisSendGrid free 100 emails/jour · Mailgun free 5 k/mois
Supervision (optionnel)0 € (UptimeRobot) ou 5 €/moisUptimeRobot free 50 monitors · Better Uptime 5 €
Antivirus serveur0 € (ClamAV)Scan des fichiers uploadés · gratuit et efficace
TOTAL an 1 réaliste (VPS)~ 100-150 €Hors ajouts premium · ~10 €/mois tout compris
⚠️Piège classique · démarrer sur AWS « parce que c'est pro » sans surveillance de facture. Un Moodle mal configuré sur AWS (RDS db.t3.medium allumée 24/7 + NAT Gateway) vous coûte vite 120 €/mois. Commencer toujours par un VPS simple, migrer vers du cloud géré seulement quand le besoin est prouvé.
💡Check-list avant de commander · (1) Nom de domaine acheté · (2) Méthode de paiement vérifiée · (3) 2FA activé sur le compte hébergeur · (4) Backup du panneau admin sauvegardé en local · (5) Tag projet avec budget mensuel max configuré (alerting).
SPRINT 2 · INSTALLATION PAS-À-PAS

🚀 De zéro à votre Moodle en ligne · ~2 heures

8 étapes concrètes · commandes exactes à copier-coller · VPS Hetzner + Ubuntu 24.04 · nginx + PHP 8.3-FPM + MariaDB · Certbot Let's Encrypt · Moodle 5.2 LTS · tests santé.

⏱ ~2 h🖥️ Ubuntu 24.04 LTS🌐 Domaine + TLS🧰 Stack LEMP
Prérequis · un moyen de paiement valide · un mot de passe fort (gestionnaire type Bitwarden) · un nom de domaine déjà disponible (ex: votre-nom.fr ou .academy) · ~2 heures devant vous.
1

Commander le VPS Hetzner CX22 · 5 €/mois

10 min
Compte Hetzner Cloud (ou OVH, Scaleway · équivalent). Créer un projet « Moodle », puis un serveur avec les caractéristiques suivantes.
Hetzner Cloud · Add Server · project "Moodle"
Configuration recommandée
Location
Falkenstein (DE) ou Nuremberg · datacenter EU · RGPD ✓
Image
Ubuntu 24.04 LTS
Type
CX22 · 2 vCPU Intel · 4 Go RAM · 40 Go NVMe
Volume
— (40 Go suffisent pour démarrer)
Networking
Public IPv4 ✓ · IPv6 ✓
SSH Keys
Ajouter votre clé publique (id_ed25519.pub)
Firewall
(on configure UFW dans le serveur)
Name
moodle-prod-01
Prix mensuel estimé (HT) : 4,51 € / mois · ~ 54 €/an
Pourquoi CX22 ? · 4 Go RAM = confort · Intel = meilleure compat Moodle que ARM · Falkenstein = datacenter EU respectueux RGPD · NVMe = MariaDB heureux.
2

Premier accès SSH & durcissement minimal

15 min
Hetzner envoie un email avec l'IP publique. On se connecte en root, on crée un utilisateur non-privilégié avec sudo, puis on désactive le root SSH.
votre-mac — ssh · 800×400
LOCAL
# Connexion initiale en root (IP reçue par email) ssh root@123.45.67.89 # Mettre tout à jour apt update && apt upgrade -y # Créer un utilisateur sudo (sans root) adduser mehrez usermod -aG sudo mehrez # Copier votre clé SSH vers le nouveau user rsync --archive --chown=mehrez:mehrez ~/.ssh /home/mehrez # TEST dans un nouvel onglet (ne pas fermer le premier !) ssh mehrez@123.45.67.89 ✓ Welcome to Ubuntu 24.04 LTS · mehrez@moodle-prod-01:~$
mehrez@moodle-prod-01 — durcissement SSH
SERVEUR
# Désactiver login root + mot de passe (clés SSH uniquement) sudo nano /etc/ssh/sshd_config.d/99-hardening.conf # Ajouter ces 3 lignes puis Ctrl+O, Enter, Ctrl+X : PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no # Recharger sshd sudo systemctl reload ssh # Installer firewall UFW basique (SSH + HTTP + HTTPS) sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable Firewall is active and enabled on system startup
Pourquoi durcir dès le début ? · un VPS public reçoit ~1 000 tentatives SSH par jour dans les minutes qui suivent son allumage. Désactiver root + password = 99 % de ces tentatives stoppées.
3

Installer la stack LEMP · nginx + PHP 8.3-FPM + MariaDB

20 min
L = Linux · E = nginx · M = MariaDB · P = PHP. Alternative à LAMP (Apache). Plus performant pour Moodle sur petit VPS.
mehrez@moodle-prod-01 — install LEMP
SERVEUR
# 1. Nginx sudo apt install -y nginx # 2. MariaDB + sécurisation interactive sudo apt install -y mariadb-server sudo mysql_secure_installation ↳ Set root password? Y · Remove anonymous users? Y · Disallow root login remotely? Y ↳ Remove test database? Y · Reload privilege tables? Y # 3. PHP 8.3-FPM + extensions Moodle requises sudo add-apt-repository -y ppa:ondrej/php sudo apt update sudo apt install -y php8.3-fpm php8.3-mysql php8.3-xml php8.3-mbstring \\ php8.3-curl php8.3-zip php8.3-gd php8.3-intl php8.3-soap php8.3-bcmath \\ php8.3-opcache php8.3-redis redis-server git unzip # 4. Tests rapides (versions) nginx -v # nginx/1.24.0 mariadb --version # mariadb 10.11 (Ubuntu 24.04) php -v # PHP 8.3.x
Pourquoi 11 extensions PHP ? · Moodle check vérifie xml · mbstring · curl · zip · gd · intl · soap · mysqli. Les autres (opcache, redis, bcmath) sont pour la performance. Oublier une extension = wizard bloqué.
4

Acheter le domaine et pointer le DNS

10 min + propagation
Registrar recommandé : OVH ou Gandi (12 €/an pour un .fr). Option bonus : passer le DNS via Cloudflare (gratuit) pour le CDN et la protection DDoS.
Registrar DNS · zone moodle.votre-nom.fr
REGISTRAR
# Créer 2 enregistrements A (IPv4) vers votre VPS Type Nom Valeur TTL A moodle 123.45.67.89 3600 A www.moodle 123.45.67.89 3600 # (Optionnel) Enregistrement AAAA pour IPv6 AAAA moodle 2a01:4f8:1c:...::1 3600 # Tester la propagation depuis votre machine locale dig moodle.votre-nom.fr +short 123.45.67.89 # propagation OK si vous voyez l'IP
TTL 3 600 s = 1 h · délai classique. La première propagation peut prendre 5 min à 2 h selon les FAI. Utilisez dig +trace pour diagnostiquer si ça traîne au-delà.
5

Certificat TLS Let's Encrypt avec Certbot

10 min
Gratuit, renouvelé automatiquement tous les 90 jours. Configure aussi le vhost nginx pour le HTTPS en une commande.
mehrez@moodle-prod-01 — certbot
SERVEUR
# Créer le vhost nginx minimal (avant TLS) sudo nano /etc/nginx/sites-available/moodle # Coller le contenu suivant : server { listen 80; server_name moodle.votre-nom.fr www.moodle.votre-nom.fr; root /var/www/moodle; index index.php; } # Activer + recharger sudo ln -s /etc/nginx/sites-available/moodle /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx nginx: configuration file /etc/nginx/nginx.conf test is successful # Installer certbot (plugin nginx) sudo apt install -y certbot python3-certbot-nginx # Générer le certificat + auto-reconfigurer nginx HTTPS sudo certbot --nginx -d moodle.votre-nom.fr -d www.moodle.votre-nom.fr # Certbot demande : email · ToS · redirect HTTP→HTTPS ? OUI ✓ Successfully received certificate ✓ Certificate will be renewed automatically (cron) # Vérifier le cron de renouvellement sudo systemctl status certbot.timer active (waiting) · next trigger: daily at 00:00
Auto-renouvellement natif · le timer systemd vérifie deux fois par jour. Les certs expirent à 90 j · renouvellement déclenché à 30 j avant expiration. Zéro intervention manuelle ensuite.
6

Télécharger Moodle 5.2 LTS et configurer nginx

15 min
Moodle HQ publie les releases sur download.moodle.org. Placement standard : /var/www/moodle pour le code, /var/moodledata pour les fichiers générés.
mehrez@moodle-prod-01 — Moodle 5.2
SERVEUR
# Cloner Moodle 5.2 (branche MOODLE_502_STABLE) cd /var/www sudo git clone -b MOODLE_502_STABLE git://git.moodle.org/moodle.git moodle # Créer le dossier de données (doit être hors webroot) sudo mkdir /var/moodledata sudo chown -R www-data:www-data /var/moodledata /var/www/moodle sudo chmod 0770 /var/moodledata # Mettre à jour le vhost nginx avec les locations Moodle sudo nano /etc/nginx/sites-available/moodle # Remplacer le contenu par : server { listen 443 ssl http2; server_name moodle.votre-nom.fr; root /var/www/moodle; index index.php; client_max_body_size 512M; # uploads devoirs # Certs gérés par certbot ssl_certificate /etc/letsencrypt/live/moodle.votre-nom.fr/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/moodle.votre-nom.fr/privkey.pem; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/run/php/php8.3-fpm.sock; include fastcgi.conf; } } sudo nginx -t && sudo systemctl reload nginx
Pourquoi moodledata hors webroot ? · sécurité. Si mal configuré, un attaquant pourrait télécharger les sauvegardes sensibles. Placer hors de /var/www + chmod 0770 = impossible via HTTP.
7

Créer la base de données et lancer le wizard Moodle

20 min
MariaDB attend son user et sa base dédiée. Ensuite, le wizard Moodle s'exécute dans le navigateur et écrit automatiquement config.php.
mehrez@moodle-prod-01 — MariaDB
SERVEUR
# Se connecter à MariaDB en root sudo mariadb # Créer base + utilisateur Moodle (UTF-8 mb4) CREATE DATABASE moodle DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'moodleuser'@'localhost' IDENTIFIED BY 'MotDePasseF0rt!XYZ'; GRANT ALL PRIVILEGES ON moodle.* TO 'moodleuser'@'localhost'; FLUSH PRIVILEGES; EXIT;

Ouvrez maintenant https://moodle.votre-nom.fr dans votre navigateur. Le wizard Moodle démarre automatiquement.

⚙️ Installation Moodle 5.2 LTS · étape 4/7 · Paramètres de base de données
1 Langue
2 Confirmation
3 Chemins
4 Base de données
5 Copyright
6 Checks
7 Install
MariaDB (native/mariadb)
localhost
moodle
moodleuser
••••••••••••••
mdl_
3306
/run/mysqld/mysqld.sock
▶ Suivant · vérification checks

Le wizard enchaîne ensuite la vérification des checks (extensions PHP, permissions, versions) puis lance l'installation des 450+ tables. Comptez 3-5 minutes.

8

Premier login admin · tests santé · cron

15 min
Moodle demande à créer le compte admin principal, puis la page d'accueil. Reste à activer le cron système pour que les tâches planifiées tournent.
mehrez@moodle-prod-01 — cron Moodle
SERVEUR
# Activer le cron Moodle (chaque minute) sudo -u www-data crontab -e # Ajouter cette ligne : * * * * * /usr/bin/php /var/www/moodle/admin/cli/cron.php > /dev/null 2>&1 # Vérifier que les tâches tournent (après 2 min) sudo tail /var/log/syslog | grep cron.php CRON[12345]: (www-data) CMD (/usr/bin/php /var/www/moodle/admin/cli/cron.php ...) # Tests santé depuis l'interface web (http://votre-domaine.fr/admin) # → Notifications admin · doit afficher zéro alerte critique # → Site admin › Server › Environment · tout en vert # → Site admin › Server › Performance · TTFB < 500 ms
Sans cron, Moodle se dégrade silencieusement · emails de notification non envoyés, files non nettoyés, stats non calculées, sessions expirées non purgées. C'est le piège débutant n°1.
💡Bonus · MUC Redis · activez le cache MUC Redis dès le J+0 via Site admin › Plugins › Caching › Configuration. Redis est déjà installé (étape 3). Gain : ×5 sur le TTFB. Voir Phase 1 Sprint 1B Étape 7 pour les détails.

✅ Vérifications finales · votre Moodle est-il vraiment prêt ?

🔍Ouvrez ces URLs dans votre navigateur et vérifiez chaque point. Si un seul échoue, corrigez avant d'ouvrir aux apprenants.

🛠 Troubleshooting · les 6 erreurs qui bloquent 90 % des installs

SymptômeCause probableRemède
Erreur 502 Bad Gateway après installPHP-FPM socket mal référencé dans nginxVérifier chemin fastcgi_pass unix:/run/php/php8.3-fpm.sock · sudo systemctl status php8.3-fpm doit être active.
Wizard · Extension non trouvéeExtension PHP manquante (ex: mbstring, intl)sudo apt install php8.3-<ext> && sudo systemctl restart php8.3-fpm · revoir étape 3 checklist complète.
Certbot · DNS problemPropagation DNS pas terminéedig +short moodle.votre-nom.fr doit renvoyer votre IP. Attendre 10-30 min après l'ajout du record A. Nouveau essai.
Moodle · moodledata pas accessiblePermissions mal poséessudo chown -R www-data:www-data /var/moodledata && sudo chmod -R 0770 /var/moodledata.
413 Request Entity Too Large uploadnginx client_max_body_size trop basDans vhost nginx : client_max_body_size 2G; · aussi post_max_size et upload_max_filesize dans /etc/php/8.3/fpm/php.ini.
Cron stagnant · last run vieille de 10 min+Crontab www-data pas sauvegardé correctementsudo -u www-data crontab -l doit afficher la ligne. Sinon relire étape 8. Vérifier /var/log/syslog pour les erreurs.
⚠️Rappel sécurité · vous avez un Moodle public sur Internet. Avant d'inviter des utilisateurs réels, passez au Sprint 3 Sécurisation (SSH clés ed25519 · fail2ban · 2FA admin Moodle · headers HTTP · politique MDP). 1 heure supplémentaire et vous êtes production-grade.
🔗Sources · docs.moodle.org/500/en/Installing_Moodle · Moodle on Ubuntu · Phase 1 Sprint 1B Étape 7 Performance pour tuning MUC Redis.
SPRINT 3 · SÉCURISATION & GO-LIVE

🔒 Passer de « ça marche » à « production-grade »

5 couches de durcissement · SSH ed25519 · fail2ban 3 jails · 2FA Moodle TOTP · 7 headers HTTP · objectif SSL Labs A+ + Mozilla Observatory A+ · 1 h de travail pour dormir tranquille.

🔐 5 couches🎯 Score A+⏱ ~1 h🛡️ Anti-brute-force

Avant le Sprint 3 · config par défaut

  • Mots de passe SSH activés · brute-force possible
  • Root login SSH encore possible (théorique)
  • Pas de détection automatique des attaques
  • Admin Moodle protégé uniquement par mot de passe
  • Pas de CSP · XSS possible
  • Pas de HSTS · downgrade HTTPS→HTTP possible
  • Score SSL Labs ~ B · Observatory ~ D

Après le Sprint 3 · production-grade

  • SSH clés ed25519 uniquement · brute-force impossible
  • Fail2ban bloque IP hostile après 3 échecs en 10 min
  • Admin Moodle protégé par 2FA TOTP (Google Auth)
  • 7 headers HTTP actifs · XSS / clickjacking mitigated
  • HSTS 1 an · protocole downgrade impossible
  • Score SSL Labs A+ · Observatory A+
  • Prêt pour audit RSSI / ISO 27001 · recette formelle
1

Clés SSH ed25519 · bannir définitivement les mots de passe

10 min
Les clés ed25519 sont plus courtes (256 bits), plus rapides et plus sûres que les RSA-2048. Standard recommandé depuis 2018.
votre-mac — generate key + push to server
LOCAL
# Générer une nouvelle paire ed25519 (si vous n'en avez pas déjà) ssh-keygen -t ed25519 -C "mehrez@moodle-prod" -f ~/.ssh/moodle_ed25519 # Un mot de passe fort sur la clé privée (agent SSH la retiendra) Enter passphrase: ••••••••••••••• # Ajouter à l'agent SSH local ssh-add ~/.ssh/moodle_ed25519 Identity added: moodle_ed25519 # Copier la clé publique sur le serveur ssh-copy-id -i ~/.ssh/moodle_ed25519.pub mehrez@moodle.votre-nom.fr # Test · on doit se logger sans mot de passe ssh mehrez@moodle.votre-nom.fr ✓ Welcome to Ubuntu 24.04 LTS
mehrez@moodle-prod-01 — sshd final hardening
SERVEUR
# Éditer la config hardening (déjà créée Sprint 2) sudo nano /etc/ssh/sshd_config.d/99-hardening.conf # Ajouter / vérifier : PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no PubkeyAuthentication yes MaxAuthTries 3 LoginGraceTime 20 AllowUsers mehrez # whitelist stricte Protocol 2 sudo systemctl reload ssh ✓ SSH hardened · password auth disabled
Pourquoi ed25519 ? · plus court (68 chars vs 380 pour RSA-4096) · plus rapide · pas de faiblesse cryptographique connue (RSA-2048 sera cassé par les ordinateurs quantiques avant ed25519).
2

Fail2ban · 3 jails pour bloquer les brute-force

15 min
Fail2ban scanne les logs en continu · détecte les IPs qui enchaînent les échecs d'authentification · les bannit temporairement via UFW.
mehrez@moodle-prod-01 — fail2ban install
SERVEUR
sudo apt install -y fail2ban sudo nano /etc/fail2ban/jail.local # Coller le contenu suivant (3 jails actives) [DEFAULT] bantime = 3600 # 1 h de bannissement findtime = 600 # fenêtre de 10 min maxretry = 3 # 3 échecs → ban banaction = ufw destemail = mehrez@votre-nom.fr action = %(action_mwl)s # Jail 1 · SSH [sshd] enabled = true port = ssh # Jail 2 · nginx auth (pour pages admin restreintes) [nginx-http-auth] enabled = true # Jail 3 · Moodle login (custom filter ci-dessous) [moodle-login] enabled = true port = http,https filter = moodle-login logpath = /var/www/moodle/../moodledata/log/auth.log maxretry = 5 bantime = 7200
filter moodle-login + redémarrage
SERVEUR
# Créer le filter Moodle (parse les logs auth Moodle) sudo nano /etc/fail2ban/filter.d/moodle-login.conf [Definition] failregex = ^.* Failed login for user .* from <HOST>.*$ ignoreregex = # Démarrer fail2ban sudo systemctl enable fail2ban sudo systemctl restart fail2ban # Vérifier les jails actives sudo fail2ban-client status Number of jail: 3 Jail list: moodle-login, nginx-http-auth, sshd # Voir les IP bannies en temps réel sudo fail2ban-client status sshd Currently banned IPs: 178.123.45.67 (Russia · 2 min ago) 91.223.144.52 (Ukraine · 18 min ago)

🛡️ Jail [sshd]

scan /var/log/auth.log · maxretry 3 · bantime 1 h

La plus importante. Moyenne en prod réelle · 200-500 IP bannies par semaine sur une instance neuve.

🛡️ Jail [nginx-http-auth]

scan /var/log/nginx/error.log · maxretry 3 · bantime 1 h

Protège les Basic Auth nginx (ex : .htaccess-like pour pages restreintes).

🛡️ Jail [moodle-login]

filter custom · maxretry 5 · bantime 2 h

Capture les échecs de login Moodle. Utile surtout si OAuth2 non activé · en SSO les échecs passent par l'IdP.

Maxretry 3 pour SSH vs 5 pour Moodle · SSH n'a aucune raison d'échouer 3 fois pour un utilisateur légitime (clé = 0 échec). Moodle peut avoir 5 échecs légitimes · typo, oubli de mot de passe.
3

2FA TOTP Moodle · Google Auth / Authy

10 min
Depuis Moodle 3.9, le 2FA TOTP est natif (plugin factor_totp). On l'active globalement et on impose pour les admins.

Parcours dans Moodle pour activer le 2FA :

  1. Site admin › Plugins › Multi-factor authentication › Manage factors
  2. Activer TOTP (Time-based One-Time Password)
  3. Activer Backup codes (codes de secours)
  4. Dans Settings · forcer MFA required for roles = Admin + Manager
  5. Admin · déconnexion / reconnexion · QR code affiché
  6. Scanner le QR dans Google Authenticator ou Authy ou 1Password
  7. Entrer le code à 6 chiffres · 2FA activée ✓
  8. Sauvegarder les 10 backup codes en lieu sûr (gestionnaire MDP)
Google Auth
moodle.votre-nom.fr
482 193
↻ expire dans 23 s
⚠️Backup codes indispensables · si vous perdez votre téléphone et n'avez pas les backup codes, vous êtes définitivement locked out du compte admin. Les coller dans Bitwarden / 1Password ou sur papier dans un coffre.
Étendre à tous les enseignants ? · non, friction trop forte pour les cours non-certifiants. Enseignants = MFA recommended (opt-in). Apprenants = en option. Règle stricte · admin/manager obligatoire · RSSI veille.
4

Headers HTTP · 7 directives qui changent tout

15 min
Les headers HTTP sont le rempart côté navigateur. Correctement configurés, ils bloquent clickjacking, XSS, downgrade HTTPS, leak referrer. Tout est dans nginx.
vhost moodle · headers de sécurité
SERVEUR
sudo nano /etc/nginx/sites-available/moodle # Dans le block server { listen 443 ... } ajouter : # 1. HSTS · force HTTPS pendant 1 an · subdomains · preload add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # 2. X-Frame-Options · anti-clickjacking add_header X-Frame-Options "SAMEORIGIN" always; # 3. X-Content-Type-Options · anti-MIME-sniffing add_header X-Content-Type-Options "nosniff" always; # 4. Referrer-Policy · limite le leak d'URL add_header Referrer-Policy "strict-origin-when-cross-origin" always; # 5. Permissions-Policy · désactive les APIs sensibles par défaut add_header Permissions-Policy "camera=(self), microphone=(self), geolocation=()" always; # 6. Content-Security-Policy · le Saint-Graal (strict mais compatible Moodle) add_header Content-Security-Policy "default-src 'self' https:; \\ script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; \\ style-src 'self' 'unsafe-inline' https:; \\ img-src 'self' data: https:; \\ font-src 'self' data: https:; \\ connect-src 'self' https:; \\ frame-ancestors 'self'" always; # 7. Cross-Origin-Opener-Policy · isolation de l'onglet add_header Cross-Origin-Opener-Policy "same-origin" always; sudo nginx -t && sudo systemctl reload nginx ✓ Headers actifs · testez sur securityheaders.com
HeaderMenace bloquéeImpact prod Moodle
Strict-Transport-SecurityHSTS max-age 1 anDowngrade HTTPS→HTTPAucun · forcer HTTPS est positif
X-Frame-Options SAMEORIGINClickjacking via iframeInterdit l'embed externe · lu par LTI cross-domain (voir CSP frame-ancestors)
X-Content-Type-Options nosniffMIME sniffing attackAucun
Referrer-Policy strict-origin...Leak URLs internesAucun côté utilisateur
Permissions-Policy camera=selfPrise de contrôle webcamProctoring natif OK (self) · plugins externes à ajuster
Content-Security-PolicyXSS, injection scripts tiersAttention · certains plugins peuvent casser · tester en staging d'abord
Cross-Origin-Opener-Policywindow.opener spoofingAucun impact utilisateur
💡CSP progressive · démarrer en mode Content-Security-Policy-Report-Only pour 1 semaine · collecter les violations via report-uri · ajuster, puis passer en actif. Évite de casser les plugins.
5

Audit final · objectif SSL Labs A+ · Observatory A+

10 min
Deux outils de référence pour valider la posture sécurité côté edge. Gratuits · reconnus mondialement · résultats publiables au RSSI.

🔍 Mozilla Observatory · score cible A+

observatory.mozilla.org · scan complet en 30 s · 10 tests
Score global
A+
105/100 attendu
CSP
Directives actives
HSTS
max-age ≥ 15 552 000
X-Frame
SAMEORIGIN
Referrer
strict-origin
Cookies
Secure · HttpOnly
Redirect
HTTP→HTTPS 301
Subresource
B
Optionnel · CDN externe

🔐 SSL Labs · score cible A+

ssllabs.com/ssltest · 2 min d'analyse · note les ciphers
Score global
A+
4 critères à 100
Certificate
100
Let's Encrypt valide
Protocol
100
TLS 1.3 + 1.2 seulement
Key Exchange
100
ECDHE · PFS
Cipher Strength
100
256-bit min
tweak nginx pour SSL Labs A+ (TLS 1.3 only)
SERVEUR
# Certbot configure déjà TLS 1.2 + 1.3. Pour A+ strict : sudo nano /etc/letsencrypt/options-ssl-nginx.conf # Remplacer la ligne ssl_protocols : ssl_protocols TLSv1.3; # supprimer TLSv1.2 si clients récents uniquement ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_stapling on; # OCSP stapling ssl_stapling_verify on; sudo nginx -t && sudo systemctl reload nginx
⚠️TLS 1.3 only · bloque les très vieux navigateurs (IE11 · Android < 7). Acceptable pour 99 % des audiences pros. Si vous avez un public très diversifié, laissez TLS 1.2 aussi (vous perdez juste A → A+).

✅ Check-list go-live · tout ce qui doit être coché avant d'ouvrir aux users

🔒 Infra & SSH

  • ☐ Clés ed25519 · PasswordAuthentication no
  • ☐ UFW actif · 22, 80, 443 seulement
  • ☐ Fail2ban · 3 jails actives (fail2ban-client status)
  • ☐ Pas de root login possible
  • ☐ Updates auto activées (unattended-upgrades)
  • ☐ Timezone serveur correcte (Europe/Paris)

🌐 HTTP & TLS

  • ☐ Certbot auto-renew timer actif
  • ☐ 7 headers HTTP configurés et rechargés
  • ☐ SSL Labs · score A+
  • ☐ Mozilla Observatory · score A+
  • ☐ HTTP redirige vers HTTPS · test curl -I
  • ☐ OCSP stapling actif

🎓 Moodle app

  • ☐ 2FA TOTP activé pour admin + manager
  • ☐ Backup codes admin sauvegardés (10)
  • ☐ Politique MDP ≥ 12 caractères + complexité
  • ☐ Self-registration désactivé (ou whitelist)
  • ☐ Debug = No debug messages (production)
  • ☐ Display debug messages = Off

🛡️ Monitoring

  • ☐ UptimeRobot · monitor 5 min
  • ☐ Alertes email fail2ban config
  • ☐ Logs nginx/PHP/MariaDB rotation active
  • ☐ Disk space alerting < 80 %
  • ☐ Backup DB nightly testé (voir Sprint 4)
  • ☐ PV recette sécurité signé (vous-même)
🎯Recette finale · une fois ces 24 cases cochées, votre Moodle perso tient le niveau de sécurité d'une instance d'entreprise. Vous pouvez ouvrir aux utilisateurs réels, publier l'URL, démarrer votre activité.
🔗Ressources · observatory.mozilla.org · ssllabs.com/ssltest · securityheaders.com · Phase 1 Sprint 1B Étape 10 pour les rappels 2FA/TLS Moodle.
SPRINT 4 · MAINTENANCE · CLÔTURE PHASE 6

🔧 Tenir votre Moodle en bonne santé · routine & playbooks

Routine quotidien/hebdo/mensuel · backups cron + rsync offsite Backblaze B2 · monitoring UptimeRobot + Grafana 6 panels · playbook P0 site down · playbook P1 perf dégradée · post-mortem template.

⏱ ~1 h/sem💾 Backup offsite📊 Dashboard 6 panels🚨 2 playbooks

📅 La routine en 3 temps · quotidien · hebdo · mensuel

80 minutes par mois bien investies évitent 80 % des incidents. La discipline > la technique.

☀️ Quotidien

Chaque matin · 5 minutes
  • Ouvrir UptimeRobot · vérifier 24 h vertsaucun incident non résolu
  • Site admin › Notifications · zéro alerte rougeCVE plugins, backups, disk
  • Fail2ban status · nouvelles IPs banniessudo fail2ban-client status sshd
  • Répondre tickets P0/P1 utilisateursdélai cible < 4 h

📅 Hebdomadaire

Chaque lundi · 15 minutes
  • Vérifier backups .mbz sur Backblaze B27 derniers jours · tailles cohérentes
  • Review Analytics predictive · apprenants at-riskdéclencher playbooks Watch/Alert
  • Check performance Grafana 7 jTTFB · CPU · RAM · disk
  • Security advisories Moodledocs.moodle.org/security
  • Review log nginx errorerreurs 5xx anormales ?
  • Rapport au COPIL si applicableKPIs de la semaine

🗓️ Mensuel

1er jour du mois · 60 minutes
  • Test restauration DR sur stagingbackup réel → nouvel environnement
  • Upgrade Moodle point release (minor)docs.moodle.org/5x/upgrading
  • Rotation clés OAuth2 / APIrotation 90 j recommandée
  • Audit comptes inactifs > 6 moissuppression RGPD ou archivage
  • Load test JMeter pic × 1,5Phase 1 Sprint 1B Étape 7
  • PV mensuel signéarchive Qualiopi / ISO
1

Backups 3-2-1 · la seule chose qui vous sauve vraiment

20 min setup · auto après
Règle 3-2-1 · 3 copies de vos données · sur 2 supports différents · dont 1 hors site. Translation Moodle · backup local + rsync Backblaze B2 + téléchargement mensuel chez vous.
moodle-prod-01 — script backup nightly
SERVEUR
sudo mkdir -p /var/backups/moodle sudo nano /usr/local/bin/moodle-backup.sh #!/bin/bash # Backup Moodle complet · DB + moodledata + code config DATE=$(date +%Y-%m-%d) BACKUP_DIR=/var/backups/moodle DB_USER=moodleuser DB_NAME=moodle RETENTION=14 # 14 jours local, 30 j sur B2 # 1. Dump DB (mariabackup bien plus rapide mais dump simple suffit ici) mysqldump --single-transaction --quick -u$DB_USER -p$MDB_PASS $DB_NAME | \ gzip > $BACKUP_DIR/db-$DATE.sql.gz # 2. Tar moodledata (exclure cache/sessions) tar --exclude='cache' --exclude='sessions' --exclude='trashdir' \ -czf $BACKUP_DIR/data-$DATE.tar.gz /var/moodledata # 3. Copy config.php (petit mais critique) cp /var/www/moodle/config.php $BACKUP_DIR/config-$DATE.php # 4. Purger > 14 j local find $BACKUP_DIR -type f -mtime +$RETENTION -delete # 5. Upload offsite sur Backblaze B2 (via rclone) rclone sync --progress $BACKUP_DIR b2:moodle-backups/ # 6. Log succès logger -t moodle-backup "Backup complete: $(du -sh $BACKUP_DIR | cut -f1)" ✓ Backup complete: 2.1G sudo chmod +x /usr/local/bin/moodle-backup.sh
rclone + B2 config + cron
SERVEUR
# Installer rclone (copie sync vers 40+ fournisseurs cloud) curl https://rclone.org/install.sh | sudo bash # Configurer Backblaze B2 (account ID + application key sur b2.backblaze.com) rclone config # n (nouveau) · name: b2 · type: Backblaze B2 · account: 0033abc... # key: K003xxx... # Tester upload rclone lsd b2: moodle-backups/ # Programmer via cron · chaque nuit à 2h du matin sudo crontab -e 0 2 * * * /usr/local/bin/moodle-backup.sh # Alerting · envoyer un email si le script échoue # (sendmail / msmtp installé · voir Phase 1 Sprint 1B Étape 6)
Pourquoi Backblaze B2 et pas S3 ? · B2 à 0,005 €/Go/mois vs S3 à 0,023 €. Pour 100 Go, c'est 0,50 €/mois vs 2,30 €. B2 + rclone = la combo préférée des admins indépendants.
⚠️Un backup non testé n'est pas un backup · chaque mois, déployer la restauration sur un VPS secondaire pour valider que le dump repart. Sinon le jour où vous en avez besoin, vous découvrez qu'il est corrompu. Test trimestriel minimum.
2

Monitoring · UptimeRobot (pulse) + Grafana (courbes)

30 min setup
UptimeRobot pour savoir « est-ce que ça répond ? » en temps réel · Grafana pour « qu'est-ce qui se passe à l'intérieur ? » · les deux gratuits pour un usage perso.

UptimeRobot (free tier) · 50 monitors gratuits · check HTTP every 5 min · alertes email/Telegram. Configurer 3 monitors · page d'accueil · admin login · endpoint de santé /admin/tool/lp/classes/external/data_for_learningplan_template_competency_summary.json.

Grafana Cloud (free tier) · 14 jours de métriques Prometheus · installer node-exporter + prometheus-mysqld-exporter sur le VPS · créer 1 dashboard avec les 6 panels ci-dessous.

Moodle · Health Overview · Last 24 h
updated 42 s ago
SLA uptime 24 h
99,95%
▲ above 99,5 target
TTFB P95
412ms
▼ -23 ms vs J-1
CPU moyen
23%
Capacity headroom 77 %
RAM utilisée
61%
Cache Redis + PHP-FPM
Requêtes DB / min
2,4k
Cache hit rate 94 %
Disk space
34%
13,6 Go / 40 Go · 26 Go libres
💡Seuils d'alerte Grafana · TTFB > 1 s pendant 5 min · CPU > 85 % pendant 10 min · RAM > 90 % · Disk > 80 % · DB queries/min > 10k · SLA < 99 %. Envoyer l'alerte vers Telegram/Slack/email perso.

🚨 Playbooks incidents · quand ça casse

P0 · CRITIQUE

Site indisponible · HTTP 5xx ou timeout

objectif MTTR < 30 min
1
Activer le mode maintenance (1 min)

sudo -u www-data php /var/www/moodle/admin/cli/maintenance.php --enable --message="Maintenance technique en cours". Message utilisateur clair, pas de panique.

2
Check services (2 min)

systemctl status nginx php8.3-fpm mariadb redis-server. Identifier quel service est down. Logs associés · journalctl -u <service> -n 100.

3
Ressources serveur (2 min)

df -h (disk) · free -h (RAM) · top (CPU/processus). 90 % des incidents : disk full ou OOM kill.

4
Logs applicatifs (5 min)

tail -200 /var/log/nginx/error.log · tail -200 /var/moodledata/log/error.log. Chercher exception Fatal PHP ou stack trace DB.

5
Action correctrice (variable)

Disk full → purge moodledata/cache + backups anciens. OOM → restart php-fpm. DB crash → mysqlcheck --repair --all-databases. Cas extrême → restore backup J-1.

6
Désactiver la maintenance (1 min)

php admin/cli/maintenance.php --disable. Vérifier page d'accueil OK. Notifier les utilisateurs dans forum/email. Planifier post-mortem dans 48 h.

P1 · DÉGRADÉ

Performance dégradée · TTFB > 2 s ou timeouts partiels

objectif MTTR < 2 h
1
Communiquer avant de diagnostiquer

Annoncer le ralentissement dans Forum d'annonces (si accessible) · rassurer que le chrono de maintenance est lancé. Users détectent les lenteurs plus vite que les alertes.

2
Localiser la contention

Grafana · CPU ? RAM ? DB ? Disk I/O ? · regarder les 6 panels. Identifier le bottleneck dominant avant de tirer sur le mauvais levier.

3
Quick wins

Purger cache Moodle · php admin/cli/purge_caches.php. Reboot php-fpm · systemctl restart php8.3-fpm. Analyser slow queries DB · SELECT * FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10.

4
Si contention CPU/RAM persistante

Scale vertical · passer du CX22 au CX32 (8 Go RAM · 8 €/mois) en 2 clics chez Hetzner · reboot · 10 min downtime. Solution court terme · planifier l'analyse racine ensuite.

5
Post-incident · cause racine

Dans les 48 h, écrire le post-mortem (template ci-dessous). Ce qui a déclenché, pourquoi la détection a été tardive, quelle action préventive à mettre en place.

📝 Post-mortem template · à remplir après chaque P0/P1

🔬 Post-Mortem · Incident <ID>

Titre court
exemple · Site indisponible 47 min suite à disk full moodledata/cache
Date / durée
exemple · 2026-04-23 14:12 → 14:59 · 47 min · 100 % impact
Détection
UptimeRobot alerte push · +8 min après le début
Résumé
Le cache Moodle a rempli le disque de 40 Go. nginx n'a plus pu écrire ses logs · 5xx. Mode maintenance à 14:35 · purge cache à 14:42 · retour online à 14:59.
Cause racine
Cache Moodle sans rotation + plugin mal codé qui invalidait le cache à chaque requête.
Timeline
14:12 dépassement disk · 14:20 UptimeRobot alerte · 14:35 intervention · 14:42 purge · 14:59 service rétabli
Impact
~350 apprenants concernés · 3 devoirs ratés · pas de perte de données · SLA hebdo tombe à 99,34 %
Actions correctives
(1) Alerte Grafana disk > 80 % · (2) Rotation cache crontab weekly · (3) Désactiver le plugin fautif · (4) Documenter runbook cache-full
Leçons
Un simple monitoring disk aurait donné 2 h d'avance. Le plugin tiers non-audité a causé la dette.
💡Culture post-mortem blameless · on cherche la cause système, pas la faute de quelqu'un. « Comment le système a permis cette erreur ? » plutôt que « Qui a fait quoi ? ». Culture Netflix / Google SRE.

🎉 Phase 6 · Hébergement personnel · TERMINÉE

Vous avez installé, sécurisé et outillé votre propre Moodle. Plus besoin d'un prestataire pour héberger votre formation · c'est désormais votre infrastructure.
🧭
Choisir hébergement
🚀
Installation VPS
🔒
Sécurisation A+
🔧
Maintenance
4 sprints livrés

Vous êtes maintenant autonome sur toute la chaîne · de la conception pédagogique (Phases 1-3) à l'exploitation technique (Phase 6). Prochaine étape · Phase 7 · Promotion professionnelle · rendre votre expertise Moodle visible sur votre réseau pro.

🔗Ressources clôture · Phase 1 Sprint 1B Étapes 6+7+12 pour le rappel backup/performance/HA · docs.moodle.org/5x/en/Security_overview · rclone.org/b2 · Grafana Cloud free tier.