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.
<note important> 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). </note>
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"
<note tip> 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). </note>
<note warning> 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. </note>
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 <nom_connexion_wifi> connection.autoconnect no
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
<note>
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.
</note>
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
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
<note important>
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.
</note>
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
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
<note tip> 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. </note>
Créer l'enregistrement A dans la zone DNS (registrar, ex: Gandi) :
Type: A Nom: peertube Valeur: <IP publique du réseau> TTL: par défaut
Vérifier la propagation :
dig peertube.votredomaine.org +short
<note important>
É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.
</note>
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).
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
<note important>
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.
</note>
<note warning>
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)”.
</note>
Vérifier :
/sbin/e-smith/db domains show peertube.votredomaine.org
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.
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
<note important>
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.
</note>
# 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).
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
<note warning> Ce changement recrée le conteneur — toute tâche de transcoding/live en cours sera interrompue. À faire hors production active. </note>
Administration → Configuration → VOD Transcoding :
<note tip> 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. </note>
<note warning>
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).
</note>
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
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.
<note warning> 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
</note>
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/<UUID>)
<note warning> 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. </note>
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).
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).
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.
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).
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).
<note warning> 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. </note>
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.