====== Déployer PeerTube derrière un serveur SME (Apache) — de A à Z ======
by thenoiser / apo33
**Résumé** : installation d'une instance PeerTube auto-hébergée (Docker) sur une machine du réseau local (ex: ThinkPad T460s), exposée publiquement via reverse proxy Apache sur le serveur SME principal (bruit), avec certificat SSL dédié et renouvellement automatique.
Ce tutoriel suppose que le serveur SME (bruit) a déjà un domaine principal fonctionnel (ici apo33.org) et qu'on ajoute un sous-domaine (peertube.apo33.org) pointant vers une machine tierce du réseau local. Pour la partie SSL automatisée en détail, voir le tuto séparé : **SSL Let's Encrypt automatisé via API Gandi (DNS-01)**.
===== 1. Vérifier les ressources disponibles =====
Avant d'installer quoi que ce soit, vérifier que la machine cible a de quoi tourner :
free -h
nproc
lscpu | grep -E "Model name|Thread"
df -h
uptime
docker ps -a 2>/dev/null
ss -tulpn | grep -E ":80|:443|:9000"
Repères officiels PeerTube : minimum 1 vCore / 1.5 Go RAM sans transcoding. Avec transcoding sur la même machine : 8 vCore / 8 Go RAM recommandés. En dessous (ex: un laptop à 2 cœurs), c'est jouable pour un usage modéré (petite structure, pas 1000 viewers simultanés), à condition de limiter les résolutions de transcodage (voir section 8).
Vérifier qu'aucun autre service lourd ne tourne déjà sur cette machine (radio Icecast/Liquidsoap, Pure Data/JACK, etc.) : ça entre directement en concurrence avec le CPU du transcoding vidéo. Idéalement, dédier la machine à PeerTube seul.
===== 2. Préparer le réseau local =====
==== 2.1 IP fixe ====
Passer la machine en IP fixe (Ethernet de préférence, pas WiFi) via NetworkManager :
nmcli connection show
sudo nmcli connection modify "Wired connection 1" \
ipv4.addresses 192.168.1.XX/24 \
ipv4.gateway 192.168.1.1 \
ipv4.dns "192.168.1.1 8.8.8.8" \
ipv4.method manual
sudo nmcli connection down "Wired connection 1"
sudo nmcli connection up "Wired connection 1"
Désactiver le WiFi si inutile :
sudo nmcli radio wifi off
sudo nmcli connection modify connection.autoconnect no
==== 2.2 Empêcher la mise en veille (si laptop) ====
sudo sed -i 's/^#HandleLidSwitch=.*/HandleLidSwitch=ignore/' /etc/systemd/logind.conf
sudo sed -i 's/^#HandleLidSwitchDocked=.*/HandleLidSwitchDocked=ignore/' /etc/systemd/logind.conf
sudo systemctl restart systemd-logind
Vérifier aussi l'historique de reboot (''last reboot'', ''journalctl --list-boots'') pour confirmer la stabilité de la machine avant de s'appuyer dessus en prod.
===== 3. Installer Docker Compose =====
docker --version
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-compose-plugin
docker compose version
===== 4. Installer PeerTube (test local d'abord) =====
mkdir -p ~/peertube && cd ~/peertube
curl -s https://raw.githubusercontent.com/Chocobozzz/PeerTube/develop/support/docker/production/docker-compose.yml -o docker-compose.yml
curl -s https://raw.githubusercontent.com/Chocobozzz/PeerTube/develop/support/docker/production/.env -o .env
Générer les secrets et écrire un ''.env'' de test en HTTP local :
PT_SECRET=$(openssl rand -hex 32)
PG_PASS=$(openssl rand -hex 16)
cat > .env << EOF
POSTGRES_USER=peertube
POSTGRES_PASSWORD=${PG_PASS}
POSTGRES_DB=peertube
PEERTUBE_DB_USERNAME=\$POSTGRES_USER
PEERTUBE_DB_PASSWORD=\$POSTGRES_PASSWORD
PEERTUBE_DB_SSL=false
PEERTUBE_DB_HOSTNAME=postgres
PEERTUBE_WEBSERVER_HOSTNAME=192.168.1.XX
PEERTUBE_WEBSERVER_PORT=9000
PEERTUBE_WEBSERVER_HTTPS=false
PEERTUBE_TRUST_PROXY=["127.0.0.1", "loopback", "172.18.0.0/16"]
PEERTUBE_SECRET=${PT_SECRET}
PEERTUBE_SMTP_HOSTNAME=postfix
PEERTUBE_SMTP_PORT=25
PEERTUBE_SMTP_FROM=noreply@votredomaine.org
PEERTUBE_SMTP_TLS=false
PEERTUBE_SMTP_DISABLE_STARTTLS=false
PEERTUBE_ADMIN_EMAIL=admin@votredomaine.org
POSTFIX_myhostname=votredomaine.org
OPENDKIM_DOMAINS=votredomaine.org=peertube
OPENDKIM_RequireSafeKeys=no
PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PUBLIC="public-read"
PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PRIVATE="private"
EOF
Le %%<<'EOF'%% (sans backslash) laisse le shell substituer ''${PT_SECRET}''/''${PG_PASS}''. Les lignes %%PEERTUBE_DB_USERNAME=\$POSTGRES_USER%% doivent garder le %%\$%% littéral (backslash devant) — c'est Docker Compose qui interprète ce %%$POSTGRES_USER%%, pas le shell.
Décommenter le port 9000 dans ''docker-compose.yml'' (nécessaire pour test local direct) :
sed -i 's|# - "9000:9000"| - "9000:9000"|' docker-compose.yml
Lancer :
docker compose up -d
docker compose logs -f --tail=50
Le mot de passe admin ''root'' généré automatiquement apparaît dans les logs — **le noter immédiatement** :
peertube-1 | info: Username: root
peertube-1 | info: User password: xxxxxxxxxxxxxxxxxxxx
Tester en local : %%http://192.168.1.XX:9000%%
===== 5. Certificat SSL dédié =====
Voir le tuto séparé **SSL Let's Encrypt automatisé via API Gandi (DNS-01)** pour obtenir un certificat ''peertube.votredomaine.org'' avec renouvellement automatique, sur la machine qui héberge PeerTube.
Une fois le certificat obtenu (''/etc/letsencrypt/live/peertube.votredomaine.org/''), créer un vhost nginx natif sur la même machine :
sudo nano /etc/nginx/sites-available/peertube.votredomaine.org.conf
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name peertube.votredomaine.org;
ssl_certificate /etc/letsencrypt/live/peertube.votredomaine.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/peertube.votredomaine.org/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
client_max_body_size 12G;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
server {
listen 80;
listen [::]:80;
server_name peertube.votredomaine.org;
return 301 https://$host$request_uri;
}
sudo ln -s /etc/nginx/sites-available/peertube.votredomaine.org.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Ce nginx local sert de terminaison SSL "locale" — utile si on veut accéder à PeerTube en HTTPS directement sur le réseau local sans repasser par le serveur SME distant. En pratique (section 7), le trafic public passera plutôt par le proxy Apache du serveur SME directement vers le port 9000 en HTTP interne — ce nginx reste disponible en secours/accès direct LAN.
===== 6. DNS public =====
Créer l'enregistrement A dans la zone DNS (registrar, ex: Gandi) :
Type: A
Nom: peertube
Valeur:
TTL: par défaut
Vérifier la propagation :
dig peertube.votredomaine.org +short
===== 7. Forwarding et reverse proxy sur le serveur SME =====
**Étape sensible.** Le serveur SME héberge potentiellement le mail et plusieurs sites de l'association. Toute modification de ''httpd.conf'' redémarre TOUT Apache d'un coup — voir la méthode de test isolé (7.5) avant d'appliquer quoi que ce soit en prod.
==== 7.1 Forwarding des ports sur la box ====
Forwarder les ports **80, 443, 1935** (RTMP live) depuis la box vers l'IP locale du serveur SME (qui fait déjà tourner Apache en frontal — c'est lui qui reçoit tout le trafic public 80/443, pas la machine PeerTube directement).
==== 7.2 Créer le domaine "vhost" côté SME ====
Sur le serveur SME, en root, via la base de données e-smith (**pas** le panneau graphique "Domains", qui ne gère que du contenu local) :
/sbin/e-smith/db domains set peertube.votredomaine.org vhost \
Description "PeerTube" \
Nameservers internet \
ProxyPassTarget http://192.168.1.XX:9000/ \
ProxyPreserveHost yes
''type'' doit être **''vhost''** (pas ''domain'') : le template ''80VirtualHosts'' traite les domaines ''type=domain'' avec un contenu local (DocumentRoot), tandis que ''81SimpleVHosts'' traite les ''type=vhost'' avec le template ''WebAppVirtualHost'', qui sait gérer ''ProxyPassTarget''.
''ProxyPreserveHost yes'' est **indispensable** : sans lui, Apache envoie au backend le Host de la cible (''192.168.1.XX:9000'') au lieu du nom public — PeerTube rejette alors les requêtes avec une erreur type "Getting client tokens for host X is forbidden (expected Y)".
Vérifier :
/sbin/e-smith/db domains show peertube.votredomaine.org
==== 7.3 Pourquoi HTTP interne plutôt que HTTPS ====
Le trafic entre le serveur SME et la machine PeerTube reste sur le réseau local — le chiffrer entre les deux n'est pas nécessaire (le vrai chiffrement public se fait déjà côté client → serveur SME). Utiliser %%http://IP:9000/%% (le port PeerTube direct, pas le nginx local) évite un problème classique : Apache vérifie par défaut la chaîne de certificat du backend (''SSLProxyVerify''), ce qui échoue si le certificat n'est pas reconnu par la CA système du serveur SME. Passer en HTTP interne élimine ce problème sans compromettre la sécurité publique.
==== 7.4 Régénérer et recharger (backup obligatoire avant) ====
cp /etc/httpd/conf/httpd.conf /root/httpd.conf.backup-$(date +%Y%m%d-%H%M%S)
/sbin/e-smith/signal-event domain-modify peertube.votredomaine.org
httpd -t
Si "Syntax OK", identifier le vrai nom du service (souvent ''httpd-e-smith'', pas ''httpd'') :
systemctl list-units --all | grep -i httpd
systemctl restart httpd-e-smith.service
systemctl status httpd-e-smith.service --no-pager
curl -Ik http://votredomaine.org
==== 7.5 Méthode de test isolé (avant toute modif touchant SSL/certificats) ====
Cette méthode est **obligatoire** dès qu'on touche à des directives SSL (SSLCertificateFile, chaînes de certificats). Une erreur de format de certificat peut faire planter tout Apache (''mod_ssl'' fatal error), avec interruption de tous les sites le temps du diagnostic.
# Copier la config réelle, ne jamais éditer l'original en direct
cp /etc/httpd/conf/httpd.conf /tmp/httpd-test.conf
# Rediriger vers des ports libres + PID file dédié, DANS le fichier lui-même
sed -i \
-e 's|^Listen 0\.0\.0\.0:80|Listen 0.0.0.0:8081|' \
-e 's|^Listen 0\.0\.0\.0:443|Listen 0.0.0.0:8444|' \
-e 's|^Listen \[::\]:80|Listen [::]:8081|' \
-e 's|^Listen \[::\]:443|Listen [::]:8444|' \
-e 's|^PidFile.*|PidFile /tmp/httpd-test.pid|' \
/tmp/httpd-test.conf
grep -n "^Listen\|^PidFile" /tmp/httpd-test.conf # vérifier avant de lancer
/usr/sbin/httpd -f /tmp/httpd-test.conf -DFOREGROUND &
sleep 3
jobs # "En cours d'exécution" = succès, config saine
tail -15 /var/log/httpd/error_log
kill %1 # arrêter le test — le service réel n'a jamais été touché
**Seulement si ce test réussit**, appliquer pour de vrai (régénérer + ''systemctl restart httpd-e-smith.service'', comme en 7.4).
===== 8. Configuration PeerTube (admin) =====
==== 8.1 Basculer le ''.env'' en mode public ====
Une fois le proxy validé :
cd ~/peertube
sed -i \
-e 's|^PEERTUBE_WEBSERVER_HOSTNAME=.*|PEERTUBE_WEBSERVER_HOSTNAME=peertube.votredomaine.org|' \
-e 's|^PEERTUBE_WEBSERVER_PORT=.*|PEERTUBE_WEBSERVER_PORT=443|' \
-e 's|^PEERTUBE_WEBSERVER_HTTPS=.*|PEERTUBE_WEBSERVER_HTTPS=true|' \
.env
docker compose up -d peertube
Ce changement recrée le conteneur — toute tâche de transcoding/live en cours sera interrompue. À faire hors production active.
==== 8.2 Transcoding VOD — réglages recommandés machine modeste ====
Administration → Configuration → VOD Transcoding :
* **Résolutions à générer** : garder le minimum utile (ex: 720p seul, ou 480p+720p). Chaque résolution ajoutée multiplie la charge CPU.
* **"Transcoder aussi la résolution originale"** : **décocher**. Génère un job de transcoding complet en pleine résolution native en plus des résolutions cochées — très coûteux, souvent redondant.
* **"Conserver une version du fichier d'origine"** : **cocher**. Garde le fichier brut uploadé, accessible en téléchargement par les visiteurs (bouton "Télécharger" sous la vidéo, propose l'original + les résolutions transcodées) — coût CPU nul, juste de l'espace disque.
* **Nombre de tâches de transcodage / Concurrence** : **1** sur une machine à 2-4 cœurs (évite que plusieurs jobs se battent pour le même CPU).
* **Sortie** : HLS avec support P2P (recommandé) — éviter de cocher aussi "Vidéos Web" en plus (double la charge et le stockage pour un gain marginal en usage interne).
**Mesure réelle observée** (CPU i5-6300U, 2 cœurs/4 threads, 720p, radio Liquidsoap tournant en parallèle) : ~1.2-1.4x le temps réel sur du contenu visuellement chargé (bruit, glitch, mouvements rapides). Sur du contenu plus posé, généralement plus proche de 0.8-1x. Le temps de transcoding scale quasi linéairement avec la durée de la vidéo source.
==== 8.3 Accélération matérielle (VAAPI/Quick Sync) — non recommandé en l'état ====
Testé et déconseillé pour l'instant : les plugins VAAPI PeerTube (''hardware-transcode-vaapi'' et forks) sont explicitement documentés comme expérimentaux, l'image Docker officielle ne contient pas les pilotes VAAPI nécessaires (il faut rebuild une image custom via Dockerfile), et plusieurs utilisateurs rapportent des échecs même avec du matériel standard. Sur un CPU faible (2 cœurs), le gain espéré ne compense pas la fragilité du montage. Optimiser plutôt via les réglages logiciels (8.2).
Pour rendre le device GPU accessible malgré tout (prérequis technique, insuffisant seul) :
ls -la /dev/dri # vérifier la présence de renderD128 sur l'hôte
Dans ''docker-compose.yml'', service ''peertube'', ajouter :
devices:
- /dev/dri:/dev/dri
==== 8.4 Formats audio (publication de sons avec image de fond) ====
Administration → Configuration → VOD Transcoding → "Autorise l'envoi de fichier audio" : coché → le fichier audio est fusionné avec une image de prévisualisation en vidéo fixe à la publication.
Formats fiables : **MP3, OGG, FLAC**.
Le **WAV** est officiellement listé comme supporté mais s'est révélé peu fiable en pratique (rejeté avec "format vidéo non supporté" sur plusieurs fichiers, y compris du PCM 16-bit standard et après ré-encodage propre). Cause exacte non identifiée. **Convertir en FLAC (lossless) ou MP3 avant upload** plutôt que de perdre du temps à déboguer un WAV récalcitrant :
ffmpeg -i fichier.wav -c:a flac fichier.flac
==== 8.5 Chat en direct (plugin livechat) ====
Installer via Administration → Extensions/Plugins → chercher "livechat" → Installer.
Configuration du plugin (Administration → Plugins → livechat → Configurer) :
Serveur Prosody : laisser l'AppImage intégrée par défaut (ne cocher "utiliser Prosody système" qu'en cas de problème avéré)
"Activer le tchat pour toutes les vidéos «non-direct»" et/ou "Activer le tchat pour tous les directs" : à cocher explicitement — ni activé par défaut, ni lié au "type de salon" (groupé par chaîne vs par vidéo), qui n'est qu'un réglage d'organisation. Sans cette case, le chat n'apparaît nulle part.
Alternative ciblée : champ "Activer le tchat pour ces vidéos" + UUID court de la vidéo (visible dans l'URL ''/w/'')
**Chat qui patine indéfiniment (rond de chargement en boucle)** : symptôme d'un échec silencieux de la connexion WebSocket à travers le reverse proxy Apache du serveur SME (visible dans les logs PeerTube par une réponse ''200'' au lieu de ''101 Switching Protocols'' sur %%/plugins/livechat/.../ws/xmpp-websocket%%). Le vhost généré par le template ''WebAppVirtualHost'' ne relaie pas les en-têtes ''Upgrade''/''Connection'' nécessaires au WebSocket.
Vérifier d'abord que Prosody tourne bien côté conteneur (élimine cette piste) : docker compose logs peertube 2>&1 | grep -i prosody | tail -20 Chercher ''Prosody is running''. Si c'est le cas, le souci est bien côté proxy, pas côté serveur de chat.
Solution sans toucher à httpd.conf sur bruit : dans la configuration du plugin livechat, section "Paramètres avancés du serveur de tchat", cocher "Désactiver Websocket". Le chat bascule automatiquement sur le protocole BOSH (polling HTTP classique), qui passe sans problème à travers le proxy existant.
===== 9. Usage de base =====
==== 9.1 Se connecter ====
%%https://peertube.votredomaine.org%% — compte ''root'' + mot de passe généré au premier démarrage (section 4). Changer le mot de passe dès la première connexion (Mon compte → Mot de passe).
==== 9.2 Publier une vidéo ====
Bouton "Publier" → glisser le fichier → renseigner titre, description, catégorie, chaîne → Publier. Le transcoding démarre automatiquement (voir 8.2 pour la durée estimée).
==== 9.3 Publier un son ====
Même bouton "Publier", sélectionner directement le fichier audio (MP3/OGG/FLAC — voir 8.4) comme fichier principal. Une fois accepté, choisir une image de prévisualisation dans les options (thumbnail) — PeerTube fusionne automatiquement à la publication.
==== 9.4 Télécharger l'original ====
Sur une vidéo publiée, bouton "Télécharger" sous le lecteur → propose toutes les résolutions générées + l'original (si "conserver le fichier d'origine" est activé, voir 8.2).
==== 9.5 Live streaming ====
Administration → Configuration → Live Streaming : activer, configurer la sauvegarde de replay. Utiliser un logiciel compatible RTMP (OBS) avec la clé de stream fournie par PeerTube sur la chaîne. Port RTMP : 1935 (forwardé en 7.1).
Le transcoding live est **obligatoire** pour du HLS (pas de mode "sans transcodage" pour le direct). Sur machine modeste, tester en conditions réelles avant un événement important — le CPU doit suivre le flux en temps réel, contrairement au VOD où un léger différé est acceptable.
===== 10. Limites connues et pistes futures =====
* **Résolution DNS interne (split-horizon)** non aboutie : en interne, ''peertube.votredomaine.org'' résout vers l'IP publique, ce qui échoue si le routeur/box ne supporte pas le NAT hairpin. Contournement actuel : ''/etc/hosts'' sur chaque poste interne (''192.168.1.XX peertube.votredomaine.org''). Piste non finalisée : activer ''Nameservers=localhost'' sur le domaine principal côté SME (''dnscache''/''tinydns''), avec les précautions nécessaires vu que c'est le domaine primaire du serveur.
* **Renouvellement du certificat côté serveur SME** : si un certificat séparé est copié manuellement sur le serveur SME (cas où le reverse proxy y sert directement le HTTPS plutôt qu'en HTTP interne), penser à resynchroniser ce fichier à chaque renouvellement (~tous les 90 jours) — pas automatique par défaut dans ce montage.
* **Machine dédiée plus puissante** : à budgétiser si l'usage grandit — un mini-PC Ryzen H/HS neuf (~350-450€) ou un PC d'occasion (Ryzen 5/i5 récent, vigilance sur le bruit/conso pour du 24/7) apporteraient un vrai confort de transcoding par rapport à un laptop ancien.