Installer un wiki MkDocs Material en conteneur LXC sur Proxmox¶
Guide d'installation manuelle, étape par étape, d'un wiki MkDocs Material en conteneur LXC Proxmox, avec mise à jour automatique sur push GitLab via un petit serveur webhook.
git push (repo contenu) → GitLab → webhook → LXC → git pull → mkdocs build → nginx
Le contenu du wiki (docs/ + mkdocs.yml) vit dans un repo Git séparé du conteneur lui-même — ce qui permet de faire évoluer le contenu indépendamment de l'infrastructure qui l'héberge.
Prérequis¶
- Un hôte Proxmox VE avec accès
pct - Un repo Git contenant (ou destiné à contenir)
docs/etmkdocs.yml - Accès internet depuis le conteneur (clone du repo, installation des paquets Python)
Valeurs d'exemple utilisées dans ce guide :
| Paramètre | Valeur d'exemple |
|---|---|
| ID du conteneur | 207 |
| IP fixe | 192.168.1.207/24 |
| Port nginx interne | 8080 |
| Port externe (NAT) | 9320 |
| Port webhook | 9900 |
| Repo contenu | git@gitlab.com:groupe/wiki.git |
1⃣ Création du conteneur LXC¶
pveam update
pveam available | grep debian-12-standard
pveam download local debian-12-standard_12.7-1_amd64.tar.zst
pct create 207 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
--hostname wiki.votre-domaine.tld \
--memory 512 \
--swap 256 \
--cores 1 \
--rootfs local-lvm:8 \
--net0 name=eth0,bridge=vmbr0,ip=192.168.1.207/24,gw=192.168.1.1 \
--unprivileged 1 \
--onboot 1
pct start 207
2⃣ Installation des paquets et de MkDocs Material¶
pct exec 207 -- apt-get update
pct exec 207 -- apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv git nginx curl openssh-client
Installer MkDocs Material dans un environnement virtuel isolé (évite les conflits avec les paquets Python système) :
pct exec 207 -- bash -c "
python3 -m venv /opt/mkdocs-venv &&
/opt/mkdocs-venv/bin/pip install --quiet --upgrade pip &&
/opt/mkdocs-venv/bin/pip install --quiet mkdocs mkdocs-material mkdocs-material-extensions pymdown-extensions
"
pct exec 207 -- ln -sf /opt/mkdocs-venv/bin/mkdocs /usr/local/bin/mkdocs
pct exec 207 -- bash -c "
mkdir -p /var/www/wiki /srv/wiki /etc/wiki /usr/local/lib/wiki /var/log/wiki &&
chown www-data:www-data /var/www/wiki
"
3⃣ Clé SSH de déploiement et clone du repo contenu¶
pct exec 207 -- bash -c "mkdir -p /root/.ssh && chmod 700 /root/.ssh"
pct exec 207 -- ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N '' -q
pct exec 207 -- ssh-keyscan gitlab.com >> /root/.ssh/known_hosts
pct exec 207 -- cat /root/.ssh/id_ed25519.pub
Ajouter cette clé publique sur GitLab : repo wiki → Settings → Repository → Deploy keys (lecture seule suffit).
Cloner et construire une première fois :
pct exec 207 -- bash -c "
git clone --branch main git@gitlab.com:groupe/wiki.git /srv/wiki &&
cd /srv/wiki && /opt/mkdocs-venv/bin/mkdocs build -d /var/www/wiki --quiet &&
chown -R www-data:www-data /var/www/wiki
"
4⃣ Script de déploiement¶
Créer /usr/local/bin/wiki-deploy.sh, appelé à chaque push :
pct exec 207 -- bash -c "cat > /usr/local/bin/wiki-deploy.sh" << 'EOF'
#!/bin/bash
set -e
WIKI_DIR=/srv/wiki
WIKI_BUILD_DIR=/var/www/wiki
WIKI_BRANCH=main
LOG=/var/log/wiki/deploy.log
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG"; }
log "Déploiement déclenché"
cd "$WIKI_DIR"
git fetch origin
git reset --hard "origin/${WIKI_BRANCH}"
log "Git pull OK ($(git rev-parse --short HEAD))"
/opt/mkdocs-venv/bin/mkdocs build -d "$WIKI_BUILD_DIR" --quiet
chown -R www-data:www-data "$WIKI_BUILD_DIR"
log "Build OK → $WIKI_BUILD_DIR"
EOF
pct exec 207 -- chmod +x /usr/local/bin/wiki-deploy.sh
5⃣ Serveur webhook¶
Un secret partagé authentifie les requêtes entrantes :
pct exec 207 -- bash -c "cat > /etc/wiki/webhook.env" << 'EOF'
WEBHOOK_SECRET=changez-moi
WEBHOOK_PORT=9900
DEPLOY_SCRIPT=/usr/local/bin/wiki-deploy.sh
EOF
pct exec 207 -- chmod 600 /etc/wiki/webhook.env
Créer /usr/local/lib/wiki/webhook.py :
pct exec 207 -- bash -c "cat > /usr/local/lib/wiki/webhook.py" << 'EOF'
#!/usr/bin/env python3
"""Webhook HTTP — reçoit les push Git et déclenche le déploiement du wiki."""
import hmac
import os
import subprocess
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
SECRET = os.environ.get("WEBHOOK_SECRET", "")
DEPLOY_SCRIPT = os.environ.get("DEPLOY_SCRIPT", "/usr/local/bin/wiki-deploy.sh")
PORT = int(os.environ.get("WEBHOOK_PORT", "9900"))
class WebhookHandler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path != "/deploy":
self._respond(404, b"Not Found")
return
token = self.headers.get("X-Gitlab-Token", "")
if not SECRET or not hmac.compare_digest(token, SECRET):
self._respond(403, b"Forbidden")
return
self._respond(200, b"OK")
threading.Thread(target=self._deploy, daemon=True).start()
def _deploy(self):
try:
subprocess.run([DEPLOY_SCRIPT], check=True, timeout=120)
except Exception as e:
print(f"[webhook] deploy error: {e}")
def _respond(self, code, body):
self.send_response(code)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, fmt, *args):
print(f"[webhook] {self.address_string()} {fmt % args}")
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", PORT), WebhookHandler)
print(f"[webhook] listening on :{PORT}")
server.serve_forever()
EOF
Service systemd associé :
pct exec 207 -- bash -c "cat > /etc/systemd/system/wiki-webhook.service" << 'EOF'
[Unit]
Description=Wiki Git Webhook
After=network.target
[Service]
Type=simple
EnvironmentFile=/etc/wiki/webhook.env
ExecStart=/usr/bin/python3 /usr/local/lib/wiki/webhook.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
pct exec 207 -- systemctl daemon-reload
pct exec 207 -- systemctl enable --now wiki-webhook
6⃣ Configuration nginx¶
pct exec 207 -- bash -c "cat > /etc/nginx/sites-available/wiki" << 'EOF'
server {
listen 8080;
server_name _;
root /var/www/wiki;
index index.html;
location / {
try_files $uri $uri/ $uri.html =404;
}
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
add_header Cache-Control "public, max-age=3600";
add_header X-Content-Type-Options nosniff;
}
EOF
pct exec 207 -- rm -f /etc/nginx/sites-enabled/default
pct exec 207 -- ln -sf /etc/nginx/sites-available/wiki /etc/nginx/sites-enabled/wiki
pct exec 207 -- nginx -t
pct exec 207 -- systemctl enable --now nginx
pct exec 207 -- systemctl restart nginx
7⃣ Configuration du webhook côté GitLab¶
GitLab → repo wiki → Settings → Webhooks
URL : http://192.168.1.207:9900/deploy
Secret : valeur de WEBHOOK_SECRET (/etc/wiki/webhook.env)
Events : Push events uniquement
8⃣ Redirection du port externe (NAT)¶
CT_IP=192.168.1.207
iptables -t nat -A PREROUTING -p tcp --dport 9320 -j DNAT --to-destination $CT_IP:8080
iptables -I FORWARD -p tcp -d $CT_IP --dport 8080 -j ACCEPT
iptables-save > /etc/iptables.rules
⚙️ Points d'administration importants¶
- Séparer contenu et infrastructure : garder
docs/+mkdocs.ymldans leur propre repo, distinct des scripts d'installation du conteneur — cela permet de versionner et de faire évoluer la documentation sans jamais toucher au conteneur. WEBHOOK_SECRETdoit être un secret fort, et identique entre/etc/wiki/webhook.envet la configuration du webhook côté GitLab — sinon les push sont rejetés (403) et le wiki ne se met plus à jour silencieusement.- Déploiement manuel en cas de besoin, sans passer par un push Git :
pct exec 207 -- /usr/local/bin/wiki-deploy.sh. - Logs à surveiller en cas de souci de mise à jour :
pct exec 207 -- tail -f /var/log/wiki/deploy.log— résultat du derniergit pull+ buildpct exec 207 -- journalctl -u wiki-webhook -f— requêtes reçues, erreurs d'authentificationpct exec 207 -- journalctl -u nginx -f— erreurs de service du site statique- Mettre à jour MkDocs Material :
pct exec 207 -- /opt/mkdocs-venv/bin/pip install --upgrade mkdocs-material, puis relancer un build (wiki-deploy.shou push Git). - Personnalisation du thème (palette, police, page d'accueil) se fait dans
mkdocs.ymletdocs/assets/css/custom.cssdu repo contenu, jamais dans les fichiers du conteneur — pour rester reconstruisible à l'identique en cas de réinstallation.
Conclusion¶
Séparer le contenu (repo wiki) de l'infrastructure (ce conteneur) permet de faire évoluer la documentation au quotidien sans jamais toucher au serveur, tout en gardant un déploiement entièrement automatisé à chaque push.