Dockers Games on Whales Installation Wolf (Multi-User Cloud Gaming) Objectif : Faire tourner plusieurs instances de Steam indépendantes sur un ou plusieurs GPU, accessibles via Moonlight avec support manettes, en utilisant Wolf et la méthode Nvidia (Manual) sur Unraid. 1. Prérequis Système (Unraid) A. Plugin Nvidia Installer le plugin "Nvidia Driver" (par ich777) depuis les Community Applications. B. Correctifs NVENC et NvFBC pour les pilotes Nvidia Nvidia bloque le nombre de flux NVENC au-delà de 3. Pour lever cette limite, on applique un patch au chargement du driver. Installer le plugin "User Scripts" depuis les Community Applications. Créer ensuite un script, par exemple nommé "Nvidia NVENC Patch" avec comme contenu : #!/bin/bash wget https://raw.githubusercontent.com/keylase/nvidia-patch/master/patch.sh -O /tmp/nvidia-patch.sh chmod +x /tmp/nvidia-patch.sh /tmp/nvidia-patch.sh À lancer une première fois manuellement. Dans User Scripts, régler le script pour qu’il s’exécute à chaque démarrage de l’Array. C. Activation du "Kernel Mode Setting" (KMS) Wolf a besoin de contrôler l’affichage au niveau du noyau pour créer les sessions graphiques. Sans ça, on risque l’écran noir. Aller dans Main > Flash > Syslinux Configuration. Dans l’entrée Unraid OS, ajouter à la fin de la ligne : nvidia-drm.modeset=1 Redémarrer Unraid. Vérifier que le KMS est bien activé : cat /sys/module/nvidia_drm/parameters/modeset Il doit renvoyer : Y D. Persistance des Manettes (uinput / uhid) Pour que Unraid recrée correctement les règles de manettes virtuelles à chaque démarrage (sinon elles disparaissent après reboot). Ouvrir le terminal Unraid. Éditer le fichier de boot : nano /boot/config/go Ajouter ce bloc à la fin du fichier : # --- Wolf : Support Manettes Virtuelles --- # 1. Permissions uinput chmod 666 /dev/uinput || true # 2. Règles UDEV cat << 'EOF' > /etc/udev/rules.d/85-wolf-virtual-inputs.rules KERNEL=="uinput", SUBSYSTEM=="misc", MODE="0660", GROUP="input", OPTIONS+="static_node=uinput" KERNEL=="uhid", TAG+="uaccess" SUBSYSTEMS=="input", ATTRS{id/vendor}=="ab00", MODE="0660", GROUP="input", ENV{ID_SEAT}="seat9" SUBSYSTEMS=="input", ATTRS{name}=="Wolf X-Box One (virtual) pad", MODE="0660", GROUP="input" SUBSYSTEMS=="input", ATTRS{name}=="Wolf PS5 (virtual) pad", MODE="0660", GROUP="input" SUBSYSTEMS=="input", ATTRS{name}=="Wolf gamepad (virtual) motion sensors", MODE="0660", GROUP="input" SUBSYSTEMS=="input", ATTRS{name}=="Wolf Nintendo (virtual) pad", MODE="0660", GROUP="input" EOF # 3. Recharger les règles udevadm control --reload-rules && udevadm trigger 2. Installation de Wolf (Méthode Nvidia Manual sur Unraid) Avec la méthode Nvidia Manual, on va : Construire une image Docker qui contient les fichiers du driver Nvidia. Pré-remplir un volume Docker nvidia-driver-vol avec ces fichiers. Monter ce volume dans le conteneur Wolf via un template Unraid. A. Construire l’image gow/nvidia-driver:latest Dans le terminal Unraid : cd /root curl https://raw.githubusercontent.com/games-on-whales/gow/master/images/nvidia-driver/Dockerfile \ | docker build -t gow/nvidia-driver:latest -f - --build-arg NV_VERSION=$(cat /sys/module/nvidia/version) . Cela crée l’image locale gow/nvidia-driver:latest avec la version de driver actuellement chargée. B. Créer / mettre à jour le volume Docker nvidia-driver-vol On pré-popule un volume Docker avec les libs Nvidia : docker create --rm \ --mount source=nvidia-driver-vol,destination=/usr/nvidia \ gow/nvidia-driver:latest sh Vérifier qu’il existe : docker volume ls | grep nvidia-driver Il doit afficher : nvidia-driver-vol C. Script User Scripts : reconstruire le volume après chaque reboot Important : à chaque mise à jour du driver Nvidia, il faut reconstruire l’image et repopuler le volume, sinon Wolf utilise des libs qui ne correspondent plus au driver chargé. Le plus simple sur Unraid : utiliser le plugin User Scripts avec un script lancé au démarrage de l’Array. Dans User Scripts, créer un script nommé par exemple "Wolf - Rebuild Nvidia driver volume". Contenu du script : #!/bin/bash # Wolf - (Re)construction de l'image et du volume driver Nvidia + restart Wolf WOLF_CONTAINER_NAME="wolf" DRIVER_IMAGE="gow/nvidia-driver:latest" DRIVER_VOLUME="nvidia-driver-vol" NOTIFY="/usr/local/emhttp/webGui/scripts/notify" notify_msg() { # $1 = niveau (normal|warning|alert) # $2 = sujet # $3 = description if [ -x "${NOTIFY}" ]; then "${NOTIFY}" -e "Wolf Nvidia" -s "$2" -d "$3" -i "$1" fi } echo ">>> [Wolf] $(date) - Début de la reconstruction Nvidia (image + volume)" notify_msg "normal" "Début mise à jour Wolf Nvidia" "Reconstruction de l'image Nvidia et du volume ${DRIVER_VOLUME} en cours..." # Vérif rapide de la version de driver en cours if [ -r /sys/module/nvidia/version ]; then DRV_VER=$(cat /sys/module/nvidia/version) echo ">>> [Wolf] Driver Nvidia détecté : ${DRV_VER}" else echo "!!! [Wolf] Impossible de lire /sys/module/nvidia/version (driver Nvidia non chargé ?)" notify_msg "warning" "Driver Nvidia introuvable" "Impossible de lire /sys/module/nvidia/version. Vérifiez que le driver est bien chargé avant de relancer le script." fi echo ">>> [Wolf] Reconstruction de l'image ${DRIVER_IMAGE}..." curl -s https://raw.githubusercontent.com/games-on-whales/gow/master/images/nvidia-driver/Dockerfile \ | docker build -t "${DRIVER_IMAGE}" -f - \ --build-arg NV_VERSION=$(cat /sys/module/nvidia/version) \ . if [ $? -ne 0 ]; then echo "!!! [Wolf] Échec du build de l'image ${DRIVER_IMAGE}" notify_msg "alert" "Échec build image Nvidia" "Le build de l'image ${DRIVER_IMAGE} a échoué. Consultez les logs du script / de Docker pour plus de détails." exit 1 fi echo ">>> [Wolf] (Re)population du volume ${DRIVER_VOLUME}..." # On crée un conteneur temporaire pour initialiser le volume, # comme dans la doc officielle Wolf, sans jamais l'exécuter. CID=$(docker create \ --mount source=${DRIVER_VOLUME},destination=/usr/nvidia \ "${DRIVER_IMAGE}" sh 2>/dev/null) if [ -z "${CID}" ]; then echo "!!! [Wolf] Échec de la création du conteneur temporaire pour le volume ${DRIVER_VOLUME}" notify_msg "alert" "Échec initialisation volume Nvidia" "Impossible de créer un conteneur temporaire pour le volume ${DRIVER_VOLUME}. Vérifiez Docker et le volume." exit 1 fi echo ">>> [Wolf] Conteneur temporaire ${CID} créé pour initialiser le volume ${DRIVER_VOLUME}" # On supprime tout de suite le conteneur (le volume, lui, reste) docker rm "${CID}" >/dev/null 2>&1 || true echo ">>> [Wolf] Conteneur temporaire ${CID} supprimé" echo ">>> [Wolf] Redémarrage du conteneur ${WOLF_CONTAINER_NAME}..." # On essaye de le stopper (au cas où il tourne déjà) if docker stop "${WOLF_CONTAINER_NAME}" 2>/dev/null; then echo ">>> [Wolf] Conteneur ${WOLF_CONTAINER_NAME} stoppé." else echo ">>> [Wolf] Conteneur ${WOLF_CONTAINER_NAME} n'était pas en cours d'exécution." fi # Puis on le démarre if docker start "${WOLF_CONTAINER_NAME}" 2>/dev/null; then echo ">>> [Wolf] Conteneur ${WOLF_CONTAINER_NAME} démarré." notify_msg "normal" "Wolf Nvidia en place" "La mise en place des bibliothèques Nvidia et le redémarrage de Wolf se sont terminés avec succès." else echo "!!! [Wolf] Impossible de démarrer le conteneur ${WOLF_CONTAINER_NAME} (à vérifier dans l'UI Docker)." notify_msg "alert" "Échec redémarrage Wolf" "Impossible de démarrer le conteneur ${WOLF_CONTAINER_NAME}. Vérifiez la configuration dans l'UI Docker." exit 1 fi echo ">>> [Wolf] $(date) - Opération terminée." Configurer le script pour s’exécuter à chaque démarrage de l’Array. À chaque reboot (après une maj de driver par exemple), le volume sera automatiquement synchronisé avec la bonne version. Ce que fait le script : - Vérifie la version du driver Nvidia actuellement chargée sur Unraid. - Rebuild l’image Docker gow/nvidia-driver:latest avec cette version de driver. - (Re)inititialise le volume Docker nvidia-driver-vol avec les librairies Nvidia de l’image. - Redémarre automatiquement le conteneur wolf pour qu’il utilise les librairies à jour. - Logue chaque étape pour faciliter le debug en cas de problème. D. Création automatique du template Unraid my-wolf.xml On va créer directement le template Unraid via une commande dans le terminal, pour qu’il apparaisse dans Docker > Add Container. Dans le terminal Unraid, lancer : mkdir -p /boot/config/plugins/dockerMan/templates-user cat << 'EOF' > /boot/config/plugins/dockerMan/templates-user/my-wolf.xml wolf ghcr.io/games-on-whales/wolf:stable https://ghcr.io/ host bash true https://games-on-whales.github.io/wolf/stable/ https://github.com/games-on-whales/wolf Wolf est un serveur de streaming Moonlight permettant de partager un hôte pour plusieurs sessions de jeu et bureaux virtuels. GameServers: http://[IP]:8080/ https://games-on-whales.github.io/assets/favicon.png -v nvidia-driver-vol:/usr/nvidia:rw --device-cgroup-rule="c 13:* rmw" 0 Wolf est un serveur de streaming Moonlight permettant de partager un hôte pour plusieurs sessions de jeu et bureaux virtuels. host /mnt/user/appdata/wolf /etc/wolf rw /var/run/docker.sock /var/run/docker.sock rw /dev /dev rw /run/udev /run/udev rw nvidia-driver-vol NVIDIA_DRIVER_VOLUME_NAME /mnt/user/appdata/wolf /var/run/docker.sock /dev /run/udev nvidia-driver-vol /dev/dri /dev/nvidiactl /dev/nvidia0 /dev/nvidia-uvm /dev/nvidia-uvm-tools /dev/nvidia-caps/nvidia-cap1 /dev/nvidia-caps/nvidia-cap2 /dev/uinput /dev/uhid EOF Aller dans Docker > Add Container et choisir le template wolf dans la liste déroulante. Normalement, tout est déjà correctement renseigné. Cliquer sur Apply pour créer et lancer le conteneur Wolf. Note GPU : ce template est prévu pour une configuration avec un seul GPU Nvidia : il expose explicitement le device /dev/nvidia0. Si tu ajoutes un second GPU, il suffira d’ajouter aussi le device /dev/nvidia1 Via l’interface Unraid : Docker → Éditer le conteneur → “Add another device” pour le rendre visible dans le conteneur Wolf. J'explique plus loin le multi GPU. (en cours de rédaction) 3. Configuration de Wolf (config.toml & utilisateurs) A. Préparation des permissions aux répertoires (méthode recommandée : ACL) Par défaut, Unraid utilise l’utilisateur nobody:users (99:100) pour les partages. Plutôt que de faire des chown/chmod -R à chaque nouvel utilisateur ou nouveau jeu, on peut utiliser les ACL pour donner en plus tous les droits au groupe 1000 (celui utilisé par les containers Wolf/Steam). Donner les droits rwx au groupe 1000 sur l’appdata Wolf setfacl -R -m g:1000:rwx /mnt/user/appdata/wolf setfacl -R -d -m g:1000:rwx /mnt/user/appdata/wolf Donner les droits rwx au groupe 1000 sur le dossier de jeux setfacl -R -m g:1000:rwx /mnt/user/Games setfacl -R -d -m g:1000:rwx /mnt/user/Games Certains outils Unraid comme “New Permissions” ou des scripts maison peuvent écraser les ACL si tu les lances sur ces dossiers → dans ce cas, il faudra relancer les setfacl. Si tu déplaces des données entre disques via certains outils externes (rsync sans -A, cp, etc.), les ACL peuvent ne pas être copiées. Modifier le config.toml Fichier cible (côté host) : nano /mnt/user/appdata/wolf/cfg/config.toml À titre perso, pour le moment, j’ai choisi d’épurer totalement l’affichage dans Moonlight. Mais il est possible de laisser l’app Wolf UI présente, qui permet de classer les apps par utilisateur. Dans ce fichier, vous supprimez tout ce qu’il y a entre : [[profiles]] id = 'moonlight-profile-id' et  [[profiles]] id = 'user' name = 'User' Et vous collez à la place la configuration de votre premier utilisateur suivante : Remplacez bien les quatre . Remplacez par une image PNG. Dimension idéale width=112px height=150px (facilement reconnaissable dans Moonlight). # --- Utilisateur 1 : --- [[profiles.apps]] icon_png_path = '' start_virtual_compositor = true title = '' [profiles.apps.runner] base_create_json = '''{ "HostConfig": { "IpcMode": "host", "CapAdd": ["SYS_ADMIN", "SYS_NICE", "SYS_PTRACE", "NET_RAW", "MKNOD", "NET_ADMIN"], "SecurityOpt": ["seccomp=unconfined", "apparmor=unconfined"], "Ulimits": [{"Name":"nofile", "Hard":10240, "Soft":10240}], "Privileged": false, "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"] } } ''' devices = [] env = [ 'PROTON_LOG=1', 'RUN_SWAY=true', 'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*', 'XKB_DEFAULT_LAYOUT=fr', 'TZ=Europe/Paris' ] image = 'ghcr.io/games-on-whales/steam:edge' mounts = [ '/mnt/user/Games:/home/retro/games-unraid:rw', '/mnt/user/appdata/wolf/:/home/retro/.steam:rw' ] name = '' ports = [] type = 'docker' 'XKB_DEFAULT_LAYOUT=fr' → clavier AZERTY français. 'TZ=Europe/Paris' → heure correcte dans l’app. '/mnt/user/Games:/home/retro/games-unraid:rw' → point de montage du répertoire d’installation des jeux. '/mnt/user/appdata/wolf/:/home/retro/.steam:rw' → rend l’installation de Steam persistante par utilisateur. 3b. Variante des gestions d’utilisateurs Wolf UI Wolf UI permet d’avoir un répertoire par utilisateur. Ranger toutes les apps par utilisateur. Utiliser le mode co-op (deux utilisateurs sur la même session en coop locale). Activation de Wolf UI Faire apparaître le menu Wolf UI dans Moonlight Pour activer Wolf UI, il faut modifier config.toml, repérer la partie : [[profiles]] id = 'moonlight-profile-id' Directement dessous, collez : [[profiles.apps]] icon_png_path = 'https://raw.githubusercontent.com/games-on-whales/wolf-ui/refs/heads/main/src/Icons/wolf_ui_icon.png' start_virtual_compositor = true title = 'Wolf UI' [profiles.apps.runner] base_create_json = '''{ "HostConfig": { "IpcMode": "host", "CapAdd": ["NET_RAW", "MKNOD", "NET_ADMIN", "SYS_ADMIN", "SYS_NICE"], "Privileged": false, "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"] } }''' devices = [] env = [ 'GOW_REQUIRED_DEVICES=/dev/input/event* /dev/dri/* /dev/nvidia*', 'WOLF_SOCKET_PATH=/var/run/wolf/wolf.sock', 'WOLF_UI_AUTOUPDATE=False', 'LOGLEVEL=INFO', 'XKB_DEFAULT_LAYOUT=fr' ] image = 'ghcr.io/games-on-whales/wolf-ui:main' mounts = [ '/var/run/wolf/wolf.sock:/var/run/wolf/wolf.sock' ] name = 'Wolf-UI' ports = [] type = 'docker' Création des espaces utilisateur De base, dans config.toml, un utilisateur user est créé avec toutes les apps dedans. Vous pouvez créer un nouvel espace en ajoutant : [[profiles]] id = 'user' name = '' icon_png_path = 'url_de_votre_image.png' pin = [ 3, 2, 1, 4 ] L' id doit être unique, exemple : user1, user2, etc... Le pin est facultatif, il permet de mettre un password de 4 chiffres pour accéder à l'espace de l'utilisateur. Toutes les apps de cet utilisateur doivent être présentes sous ces lignes. 4. Configuration Post-Installation A. Appairage Moonlight Lancer Moonlight sur le client (PC, TV, Tablette). Noter le code PIN affiché. Consulter les logs Docker : docker logs -f wolf Cliquer sur le lien http://IP_UNRAID:47989/pin/ID visible dans les logs et entrer le code PIN à 4 chiffres affiché dans Moonlight. La page web vous demande de renseigner le PIN pour déverrouiller Moonlight. À la première ouverture de Steam, le dépôt s’installe, puis il faut vous connecter à votre compte avec Steam Guard. B. Déclaration des jeux dans Steam Par défaut, Steam ne connaît pas le disque d’installation des jeux configuré dans config.toml. Si vous laissez tel quel, il va installer les jeux dans le répertoire appdata (non partagé entre utilisateurs). Fermer Big Picture. Dans Steam, aller dans Paramètres > Stockage. Ajouter un lecteur > remonter à /Home > sélectionner le dossier /games-unraid. Le définir comme lecteur par défaut (étoile). À faire pour chaque compte Steam. C. MangoHud L’overlay Steam ne fonctionne pas dans le conteneur Wolf. Pour voir les FPS et autres stats, on utilise MangoHud. Il est déjà installé et activé par défaut pour tous les jeux Vulkan (y compris les jeux Proton). Jeux OpenGL (Linux natif) Pour les jeux OpenGL, vous pouvez l’activer jeu par jeu en ajoutant : mangohud %command% Activation et configuration En jeu, vous pouvez appuyer sur Maj droite + F12 pour afficher/masquer MangoHud. Et sur Maj droite + F11 pour changer sa position à l’écran. 5. Accès à Distance (Ouverture de Ports) Wolf ne gère pas l’UPnP. Pour jouer depuis l’extérieur, deux options : Option A : VPN (Recommandé) Utiliser Tailscale ou Wireguard (plugins Unraid). C’est plus sécurisé et ne nécessite aucune ouverture de port. (Plus chiant côté FireTV ou autre, mais c’est la vie 😄) Option B : Ouverture de Ports (NAT) Sans VPN, rediriger les ports suivants sur votre routeur vers l’IP du serveur Unraid : TCP 47984 : HTTPS (sécurité) TCP 47989 : HTTP (appairage Web) TCP 48010 : RTSP (initialisation du stream) UDP 47999 : Contrôle (manettes) UDP 48100 : Flux vidéo (RTP - spécifique Wolf) UDP 48200 : Flux audio (RTP - spécifique Wolf) Applications Voici la liste des applications utilisables. Heroic Games Launcher Un lanceur open source pour gérer et jouer aux jeux Epic Games Store, GOG et Amazon Prime Games sous Linux. Configuration par défaut Voici le bloc de configuration par défaut qui sera utilisé dans Wolf. Vous pouvez le remplacer en éditant le fichier config.toml. [[profiles.apps]] icon_png_path = 'https://games-on-whales.github.io/wildlife/apps/heroic-games-launcher/assets/icon.png' start_virtual_compositor = true title = 'Heroic' [profiles.apps.runner] base_create_json = '''{ "HostConfig": { "IpcMode": "host", "CapAdd": ["SYS_ADMIN", "SYS_NICE", "SYS_PTRACE", "NET_RAW", "MKNOD", "NET_ADMIN"], "SecurityOpt": ["seccomp=unconfined", "apparmor=unconfined"], "Ulimits": [{"Name":"nofile", "Hard":10240, "Soft":10240}], "Privileged": false, "DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"] } }''' devices = [] env = [ 'RUN_SWAY=true', 'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*' ] image = 'ghcr.io/games-on-whales/heroic-games-launcher:edge' mounts = [] name = 'WolfHeroic' ports = [] type = 'docker' Pensez à ajouter dans les variables d'environnement : 'XKB_DEFAULT_LAYOUT=fr' pour le clavier AZERTY Si vous avez suivi mon tutoriel pour Wolf. Il faudra ajouter les points de montage suivant mounts = [             '/mnt/cache/Games:/home/retro/games-unraid:rw',             '/mnt/user/appdata/wolf/:/home/retro/.heroic:rw'         ] Premier lancement Voici l'interface au démarrage de Heroic. Association des comptes Connecter vos différents comptes EPIC / GOG / Amazon, afin de récupérer vos bibliothèques de jeux. Ajout du répertoire d'installation des jeux Nous allons maintenant faire en sorte que les jeux installés depuis Heroic se fassent dans le même répertoire commun. Allez dans le menu Options puis Général. Dans la section Chemin d'installation par défaut, mettez /home/retro/games-unraid C'est le chemin du répertoire que nous avons utilisé dans le tuto d'installation de Wolf. Il faudra l'adapter si vous avez choisi un autre répertoire. Utilisation de GE-PROTON Pour faire fonctionner les jeux Windows (exécutable en .exe). Nous allons utiliser PROTON. J'ai noté une meilleure compatibilité qu'avec Wine qui est utilisé par défaut dans Heroic. Allez dans le menu Gestion de Wine Choisir l'onglet GE-Proton Cliquez sur la flèche de téléchargement pour la version GE-Proton-Latest Utilisation de Proton par dédaut : Allez dans le menu Options puis Paramètre de jeu par défaut. Choisissez Proton - GE - Proton - Latest dans la version de Wine Ajout des jeux Heroic dans Steam Dans le but de simplifier l'accès à notre bibliothèque de jeux. Nous allons ajouter les jeux téléchargés depuis les différentes plateformes, à Steam. Ce qui permettra de centraliser sur Steam l'ensemble de nos jeux. Au lancement de Steam, quittez le mode Big Picture. Allez dans le menu Steam puis Paramètres. Dans les paramètres, allez dans le menu Bibliothèque, ensuite cliquez Ajouter un jeu non Steam Cliquez sur Parcourir... Et naviguez dans /home/retro/games-unraid (le répertoire de notre tuto) Sélectionner les .exe des jeux que vous voulez importer dans Steam. Ne pas oublier de cliquer Ajouter la sélection une fois fait. Les jeux seront désormais dans la section NON STEAM de votre bibliothèque. Prérequis pour lancer les jeux Si vos jeux sont des installations Windows (executable en .exe). Il faudra les forcer en compatibilité PROTON Dans Steam, allez dans les Propriétés... de votre jeu. Dans le menu Compatibilité, cochez forcer l'utilisation d'un outil... Et choisissez Proton Expérimental Dolphin pour RomM RomM — Emulator Streaming avec Dolphin sur Unraid (GPU NVIDIA) Ce tutoriel explique comment ajouter un conteneur Dolphin (GameCube/Wii) à une instance RomM 5.1.0+ existante pour utiliser la fonctionnalité Emulator Streaming : lancer un jeu depuis l'interface RomM et y jouer dans le navigateur, avec rendu et encodage sur le GPU NVIDIA. Architecture : RomM ne pilote pas Dolphin directement. Un Docker Mod (maintenu par LoneAngelFayt) injecte dans le conteneur Dolphin un broker HTTP (port 8000) que RomM contacte pour lancer/piloter les jeux. L'image et le son sont diffusés par Selkies (le bureau web intégré aux images LinuxServer), en H.264 NVENC. ⚠️ Testé avec : Unraid 7, RTX 3060, driver NVIDIA 610.x, lscr.io/linuxserver/dolphin:latest (base Selkies/Wayland), RomM 5.1.0. 1. Prérequis Unraid 1.1 Plugin Nvidia Driver Installer le plugin Nvidia Driver (Community Applications). Driver propriétaire 580 ou supérieur requis. LinuxServer recommande la branche Production pour Unraid. Le GPU ne doit pas être réservé au VFIO (il doit être disponible pour Docker). Sur un serveur headless (sans écran), la doc LinuxServer indique qu'un dummy plug HDMI/DP branché sur la carte est requis pour que DRM s'initialise correctement. 1.2 Paramètres noyau (Syslinux) Menu Main → Flash → Syslinux Config (vue Raw), ajouter à la fin de la ligne append de l'entrée « Unraid OS » (et « Unraid OS GUI Mode » si utilisée) : nvidia-drm.modeset=1 nvidia_drm.fbdev=1 Redémarrer le serveur, puis vérifier : cat /proc/cmdline # les 2 paramètres doivent apparaître cat /sys/module/nvidia_drm/parameters/modeset # doit renvoyer Y nvidia-smi -L # liste le(s) GPU + UUID Notez l'UUID du GPU ( GPU-xxxxxxxx-...), il servira dans le template. 1.3 Limite inotify (optionnel mais recommandé) Les images Selkies consomment beaucoup d'instances inotify. Pour éviter l'avertissement « Too many open files » : sysctl -w fs.inotify.max_user_instances=1024 echo 'sysctl -w fs.inotify.max_user_instances=1024' >> /boot/config/go 2. ⚠️ Correctifs NVIDIA indispensables (Unraid) Le runtime NVIDIA d'Unraid présente deux trous d'injection vers les conteneurs. Sans eux, l'émulateur tourne en rendu logiciel (très lent) ou affiche un écran noir. Les deux correctifs sont simples et persistants. 2.1 Composant egl-x11 (rendu OpenGL/EGL) C'est le point qui fait échouer la plupart des installations (symptôme : interface Dolphin visible, mais zone de jeu noire avec le son, ou jeu qui rame énormément en rendu logiciel). Le runtime NVIDIA d'Unraid injecte dans les conteneurs les plateformes EGL wayland, wayland2 et gbm, mais pas les deux plateformes X11 ( xlib/ xcb) — pourtant livrées par le driver et indispensables à Dolphin, qui crée son contexte OpenGL via EGL sur X11. Sans elles, l'EGL NVIDIA ne peut pas créer de surface et Dolphin retombe en rendu logiciel (ou écran noir via Zink : MESA: error: zink: could not create swapchain). Vérifier que le driver de l'hôte fournit bien les fichiers : ls /var/local/overlay/usr/lib64/ | grep egl-x ls /var/local/overlay/usr/share/egl/egl_external_platform.d/ | grep -E "xlib|xcb" Vous devez voir libnvidia-egl-xlib.so.1.X.X, libnvidia-egl-xcb.so.1.X.X, 20_nvidia_xlib.json et 20_nvidia_xcb.json. Notez le numéro de version exact des .so (ex. 1.0.5) : il est utilisé dans les montages ci-dessous, et il change à chaque mise à jour du driver (voir §7). Le correctif consiste en 4 montages en lecture seule + 1 variable, intégrés au template Dolphin de la section suivante. 2.2 Nœud /dev/nvidia-modeset (présentation Vulkan) Le toolkit injecte nvidia0, nvidiactl, nvidia-uvm, nvidia-uvm-tools et nvidia-caps… mais pas /dev/nvidia-modeset, qui est nécessaire à la présentation Vulkan (l'énumération des GPU, elle, fonctionne sans lui — d'où des diagnostics trompeurs). Symptômes sans ce nœud : vulkaninfo liste bien la carte mais vkGetPhysicalDeviceSurfacePresentModesKHR échoue en ERROR_UNKNOWN ; vkcube sélectionne le GPU puis plante à la création de la swapchain ; Dolphin en backend Vulkan ou via Zink affiche un écran noir avec le son. Vérification : ls /dev/nvidia-modeset # sur l'hôte : présent docker exec dolphin ls /dev/nvidia-modeset 2>&1 # dans le conteneur : absent Correctif : ajouter --device /dev/nvidia-modeset aux Extra Parameters (voir §3.4). 3. Conteneur Dolphin Ajouter un conteneur avec l'image lscr.io/linuxserver/dolphin et la configuration suivante. 3.1 Générer un secret partagé Ce secret authentifie les échanges RomM ↔ broker. Générez-le une fois : openssl rand -hex 32 3.2 Ports (host:container) Port hôte Port conteneur Rôle 3000 3000 Selkies HTTP (peu utile) 3001 3001 Selkies HTTPS (le flux de jeu) 8000 8000 Broker RomM Adaptez les ports hôte s'ils sont déjà pris par un autre conteneur (dans ce cas, reportez le port choisi dans le bloc streaming du §4.2). 3.3 Variables d'environnement Variable Valeur Remarque DOCKER_MODS ghcr.io/loneangelfayt/dolphin-romm-integration-mod:latest installe le broker ROM_ROOT /romm/library racine de la bibliothèque vue par le broker BROKER_SECRET (votre secret) même valeur que côté RomM NVIDIA_VISIBLE_DEVICES (UUID du GPU) ou all si un seul GPU NVIDIA_DRIVER_CAPABILITIES all DRINODE /dev/dri/renderD128 GPU de rendu (vérifier avec ls -l /dev/dri/by-path/ que c'est bien la carte NVIDIA) DRI_NODE /dev/dri/renderD128 GPU d'encodage — identique = mode Zero-Copy SELKIES_MANUAL_WIDTH 1920 bride la résolution du flux SELKIES_MANUAL_HEIGHT 1080 sinon elle suit la fenêtre du navigateur (coûteux) __EGL_VENDOR_LIBRARY_FILENAMES /usr/share/glvnd/egl_vendor.d/10_nvidia.json force l'EGL NVIDIA (fait partie du correctif §2) PUID / PGID 99 / 100 standard Unraid 3.4 Extra Parameters --gpus all --runtime nvidia --shm-size=1gb --device /dev/nvidia-modeset --cpuset-cpus=0-5,12-17 --device /dev/nvidia-modeset : correctif §2.2. --cpuset-cpus : épinglage CPU, obligatoire sur tout Ryzen multi-CCD — sans lui les performances sont fortement dégradées (voir §7). Adaptez impérativement la liste à votre processeur : la valeur ci-dessus correspond au premier CCD d'un 3900X et sera contre-productive sur une autre topologie. Sur un CPU mono-CCD ou Intel, retirez l'option. 3.5 Volumes Host Path Container Path Mode (votre dossier de ROMs — le même que celui monté dans RomM) /romm/library ro (votre appdata, ex. /mnt/user/appdata/dolphin) /config rw /var/local/overlay/usr/lib64/libnvidia-egl-xlib.so.1.X.X /usr/lib64/libnvidia-egl-xlib.so.1 ro /var/local/overlay/usr/lib64/libnvidia-egl-xcb.so.1.X.X /usr/lib64/libnvidia-egl-xcb.so.1 ro /var/local/overlay/usr/share/egl/egl_external_platform.d/20_nvidia_xlib.json /usr/share/egl/egl_external_platform.d/20_nvidia_xlib.json ro /var/local/overlay/usr/share/egl/egl_external_platform.d/20_nvidia_xcb.json /usr/share/egl/egl_external_platform.d/20_nvidia_xcb.json ro Remplacez 1.X.X par la version relevée au §2. Le Container Path des libs est volontairement le nom court .so.1 : c'est celui que le loader EGL recherche (déclaré dans les JSON), ce qui évite d'avoir à créer des symlinks. 💡 Important : le chemin de la bibliothèque doit être identique dans RomM et dans Dolphin ( /romm/library des deux côtés), sinon le broker ne retrouvera pas les fichiers que RomM lui demande de lancer. 3.6 Vérifications après démarrage docker logs dolphin 2>&1 | grep -iE "broker|NVENC|Zero-Copy" Attendu : ROM broker listening on port 8000, Shared secret auth enabled, NVENC ... Initialized successfully, Decision: Zero-Copy path active. curl -s http://localhost:8000/health # → {"status":"ok"} docker exec dolphin sh -c 'ldconfig -p | grep egl-x' # → les 2 libs xlib/xcb 4. Côté RomM 4.1 Variable d'environnement Ajouter au conteneur RomM : Variable Valeur STREAMING_BROKER_SECRET (le même secret qu'au §3.1) 4.2 config.yml Dans le config.yml de RomM (dossier config), ajouter le bloc streaming (adapter l'IP du serveur et les ports choisis) : streaming: enabled: true containers: - platform: ngc host: https://IP_DU_SERVEUR:3001 broker_host: http://IP_DU_SERVEUR:8000 label: Dolphin memory_card_sync: true - platform: wii host: https://IP_DU_SERVEUR:3001 broker_host: http://IP_DU_SERVEUR:8000 label: Dolphin Redémarrer RomM après modification. 4.3 Structure de la bibliothèque Le dossier de plateforme doit correspondre au fs_slug de RomM : ngc pour la GameCube, wii pour la Wii (pas gc, pas gamecube) — sinon prévoir un mappage dans config.yml. Les ROMs doivent être des fichiers directs ( .rvz, .iso) — pas d'archives .zip/ .7z, Dolphin ne peut pas les lancer via le broker. ⚠️ RomM supporte deux structures ( library/roms// ou library//roms/) mais la simple présence d'un dossier library/roms/ fait basculer toute la bibliothèque sur la première : ne mélangez pas les deux, ou toutes vos autres plateformes deviendront invisibles au scan. Rescanner la plateforme dans RomM après tout changement. 5. Premier lancement Ouvrir une première fois https://IP_DU_SERVEUR:3001 et accepter le certificat auto-signé (sinon le flux ne se chargera pas dans RomM). Dans RomM, ouvrir la fiche d'un jeu GameCube → bouton Play on Dolphin. Le jeu doit s'afficher, fluide, avec le son. Vérifier que le GPU est bien utilisé (pendant qu'un jeu tourne) : docker exec -u abc dolphin sh -c 'for p in /proc/[0-9]*; do ls -l $p/fd 2>/dev/null | grep -q nvidia && echo "$(basename $p) $(tr "\0" " " < $p/cmdline | cut -c1-60)"; done' dolphin-emu doit apparaître dans la liste. S'il n'y a que selkies, labwc et Xwayland, Dolphin est en rendu logiciel → revoir le §2. Note : lancez bien ce test avec -u abc — en root, la lecture de /proc//fd des processus de l'utilisateur abc échoue silencieusement. 6. Réglages Dolphin utiles Le fichier de configuration est /.config/dolphin-emu/Dolphin.ini. Toujours éditer conteneur arrêté (Dolphin réécrit ses fichiers en se fermant), et savoir que le mod réimpose GFXBackend = OpenGL à chaque recréation du conteneur — c'est le bon backend ici (c'est le chemin EGL corrigé au §2), ne pas chercher à le changer. ⚠️ Après toute édition depuis l'hôte ( sed -i, cat >, redirection…), rétablissez le propriétaire : chown -R 99:100 . Ces commandes peuvent recréer le fichier en root:root, et l'émulateur ne peut alors plus enregistrer sa configuration — silencieusement, à part une ligne d'erreur dans son log. 6.1 Performances Réglage recommandé (section [Core]) : CPUThread = True C'est le mode « Dual Core » de Dolphin ; selon les versions il peut être désactivé par défaut, et son absence coûte très cher en performances. 6.2 Jeux en français La langue de la console émulée (celle que les jeux GameCube utilisent) se règle dans [Core] : docker stop dolphin F=/.config/dolphin-emu/Dolphin.ini sed -i '/^SelectedLanguage *=/d' "$F" sed -i '/^\[Core\]/a SelectedLanguage = 2' "$F" chown -R 99:100 docker start dolphin grep -n "^SelectedLanguage" "$F" Valeurs : 0 anglais, 1 allemand, 2 français, 3 espagnol, 4 italien, 5 néerlandais. Cette clé survit aux redémarrages et aux recréations du conteneur : le mod ne la touche pas. ⚠️ N'ajoutez pas [Interface] LanguageCode pour traduire l'interface de Dolphin : dans cette image, la clé provoque une boîte de dialogue d'erreur de langue à chaque démarrage. Les jeux sont de toute façon en français grâce à SelectedLanguage, ce qui est l'essentiel — l'interface de l'émulateur n'est presque jamais visible pour les joueurs (le broker lance directement le jeu en plein écran). Jeux Wii : la langue ne vient pas de Dolphin.ini mais de la NAND Wii émulée. Elle se règle dans l'interface (Options → Configuration → Wii → Langue du système) et persiste dans l'appdata. 7. ⚠️ Épinglage CPU — obligatoire sur Ryzen multi-CCD Sur les Ryzen à plusieurs CCD (3900X, 3950X, 5900X, 5950X, 7900X, 7950X…), les cœurs sont répartis en groupes ayant chacun son propre cache L3. Un émulateur dont les threads dialoguent en permanence perd beaucoup quand l'ordonnanceur les fait migrer d'un groupe à l'autre : les échanges passent alors par l'Infinity Fabric, bien plus lent. Ce n'est pas une optimisation facultative : sans épinglage, comptez une perte de l'ordre d'un tiers des performances, suffisante pour rendre injouables les titres exigeants. Le symptôme est déroutant — performances médiocres alors que rien ne sature (CPU global bas, GPU à 10 %, aucun thread à 100 %) — parce que les threads n'attendent pas du calcul, mais la mémoire. Identifiez vos groupes de cache : lscpu -e=CPU,CORE,L3 Les CPU partageant le même numéro de L3 (ou deux L3 voisins d'un même CCD) forment un groupe. Exemple sur un 3900X : le premier CCD regroupe les CPU 0-5 et leurs hyperthreads 12-17. Épinglez ensuite le conteneur sur un seul CCD via --cpuset-cpus=0-5,12-17 (Extra Parameters), ou l'onglet CPU Pinning du template Unraid. Le gain observé sur ce type de configuration va de quelques pourcents à +50 %. C'est aussi ce qui explique qu'une VM avec vCPU épinglés paraisse bien plus rapide que le même émulateur en conteneur non épinglé, sur la même machine. Multi-conteneurs : si Dolphin et Eden tournent simultanément, répartissez-les sur des groupes différents plutôt que de les faire cohabiter sur le même CCD. 8. Maintenance et limites connues À chaque mise à jour du driver NVIDIA de l'hôte, le nom des libs ( libnvidia-egl-*.so.1.X.X) change : mettre à jour les 2 Host Path correspondants dans le template Dolphin, sinon écran noir / rendu logiciel au retour. Une seule session à la fois par conteneur : un conteneur = une instance Dolphin = un flux. Pour du multi-joueurs simultané, dupliquer le conteneur (autres ports, autre secret possible) et ajouter une entrée dans streaming.containers. Sessions fantômes : fermer l'onglet du navigateur ne tue pas Dolphin ; la session peut rester occupée. En attendant mieux : docker exec dolphin pkill dolphin-emu. Les paquets installés à la main dans le conteneur ( apt-get) ne survivent pas à une recréation. Pour des outils de diagnostic permanents, chaîner le mod universal-package-install : DOCKER_MODS=ghcr.io/loneangelfayt/dolphin-romm-integration-mod:latest|linuxserver/mods:universal-package-install + INSTALL_PACKAGES=vulkan-tools|mesa-utils|x11-utils. 9. Dépannage rapide Symptôme Cause probable Solution « ROMs not found » au scan Nom de dossier ≠ fs_slug, ou structures mélangées §4.3 Pas de bouton « Play on Dolphin » Bloc streaming absent/invalide, ou secret différent §4.1–4.2 Flux ne se charge pas dans RomM Certificat auto-signé jamais accepté §5.1 Jeu très lent, dolphin-emu absent du test des fd Rendu logiciel (egl-x11 manquant) §2.1 Zone de jeu noire, interface et son OK Présentation Vulkan impossible ( /dev/nvidia-modeset manquant) §2.2 Jeu lent alors que rien ne sature (CPU bas, GPU ~10 %) Threads migrant entre CCD §7 Écran noir après une mise à jour du driver Version des libs egl-x11 changée §8 RomM renvoie 502 au lancement L'émulateur crashe au démarrage : le broker abandonne après 5 essais. Voir /dolphin.log ci-dessous qt.qpa.xcb: could not connect to display :0 dans dolphin.log Sockets X périmées accumulées dans /tmp/.X11-unix (le broker détecte un affichage mort) Recréer le conteneur — un simple redémarrage conserve /tmp Boîte d'erreur de langue au démarrage Clé [Interface] LanguageCode présente La supprimer (§6.2) « Failed to initialize video backend » Variables graphiques parasites ou backend forcé à Vulkan Revenir à la config §3, GFXBackend = OpenGL Eden pour RomM RomM — Emulator Streaming avec Eden (Nintendo Switch) sur Unraid (GPU NVIDIA) Ce tutoriel explique comment ajouter un conteneur Eden (émulateur Nintendo Switch, fork de Yuzu) à une instance RomM 5.1.0+ pour lancer des jeux Switch depuis RomM et y jouer dans le navigateur, avec rendu GPU NVIDIA. Le principe est identique à l'intégration Dolphin : un Docker Mod injecte un broker HTTP (port 8000 du conteneur) que RomM contacte, et le flux est diffusé par Selkies en H.264 NVENC. ✅ Testé et fonctionnel avec RomM 5.1.0 stable (la branche de développement n'est pas nécessaire), Unraid 7, Ryzen 9 3900X + RTX 3060, lscr.io/linuxserver/eden. Résultat : Mario Kart 8 Deluxe à 60 FPS stables en 1080p dans le navigateur. 📖 Ce guide suppose que la page « Emulator Streaming avec Dolphin » a déjà été suivie : les prérequis hôte sont les mêmes et ne sont pas re-détaillés ici — plugin Nvidia Driver (branche Production, ≥ 580), paramètres noyau nvidia-drm.modeset=1 nvidia_drm.fbdev=1, et surtout les deux correctifs NVIDIA (§2 de la page Dolphin) : composant egl-x11 et nœud /dev/nvidia-modeset, indispensables ici aussi. 🌐 Ce guide décrit une installation en réseau local. Pour jouer depuis l'extérieur, pour l'isolation réseau des conteneurs, et pour le portail nginx du mod Eden qui bloque le streaming avec RomM 5.1.0, voir la page « Accès distant, publication HTTPS et isolation réseau ». Elle contient aussi l'avertissement de sécurité à lire avant toute publication. 1. Particularités d'Eden à connaître avant de commencer Contenu requis de votre propre console. Eden ne lance aucun jeu commercial sans les prod.keys (clés de déchiffrement) et le firmware de la Switch — à extraire de votre propre console. Eden est Vulkan-first, et son backend Vulkan a besoin du nœud /dev/nvidia-modeset (§2.2 de la page Dolphin). Sans lui : liste de GPU vide dans les menus, et au lancement d'un jeu l'erreur « Eden has encountered an error while running the video core » (log : Invalid device index -1, VK_ERROR_INITIALIZATION_FAILED). Les réglages faits dans l'interface ne sont jamais sauvegardés quand Eden est lancé par le broker : celui-ci tue le processus avant qu'Eden n'écrive sa configuration. Tout réglage durable se fait dans le fichier, conteneur arrêté (§5). Deux réglages par défaut coûtent très cher en performances : la précision GPU sur High et l'absence d'épinglage CPU sur les Ryzen multi-CCD (§5 et §6). Sans eux, comptez ~30 FPS au lieu de 60. 2. Conteneur Eden Image : lscr.io/linuxserver/eden. Configuration identique à Dolphin, à trois différences près : le mod, les ports hôte (les deux conteneurs coexistent), et l'appdata dédié. 2.1 Ports (host:container) Les services écoutent toujours sur 3000/3001/8000 côté conteneur — seuls les ports hôte changent : Port hôte (exemple) Port conteneur Rôle 3010 3000 Selkies HTTP 3011 3001 Selkies HTTPS (flux de jeu) 8001 8000 Broker RomM 2.2 Variables d'environnement Variable Valeur DOCKER_MODS ghcr.io/loneangelfayt/eden-romm-integration-mod:latest ROM_ROOT /romm/library BROKER_SECRET le même secret que vos autres conteneurs émulateurs — RomM n'a qu'un STREAMING_BROKER_SECRET global NVIDIA_VISIBLE_DEVICES UUID du GPU (ou all) NVIDIA_DRIVER_CAPABILITIES all DRINODE / DRI_NODE /dev/dri/renderD128 (adapter) SELKIES_MANUAL_WIDTH / HEIGHT 1920 / 1080 __EGL_VENDOR_LIBRARY_FILENAMES /usr/share/glvnd/egl_vendor.d/10_nvidia.json PUID / PGID 99 / 100 2.3 Extra Parameters --gpus all --runtime nvidia --shm-size=1gb --device /dev/nvidia-modeset --cpuset-cpus=0-5,12-17 L'épinglage --cpuset-cpus est obligatoire sur tout Ryzen multi-CCD : sans lui, comptez environ un tiers de performances en moins (§6). Adaptez impérativement la liste à votre processeur — la valeur ci-dessus correspond au premier CCD d'un 3900X et serait contre-productive sur une autre topologie. Sur un CPU mono-CCD ou Intel, retirez l'option. 2.4 Volumes Host Path Container Path Mode (le même dossier de ROMs que RomM) /romm/library ro (appdata dédié, ex. /mnt/user/appdata/eden) /config rw + les 4 montages du correctif egl-x11 (identiques à la page Dolphin : 2 libs + 2 JSON) ro Rappel : le dossier de bibliothèque doit être monté au même chemin conteneur ( /romm/library) dans RomM et dans Eden. 2.5 Vérification du broker docker logs eden 2>&1 | grep -iE "broker|mod" | head -10 curl -s http://localhost:8001/health # → {"status": "ok"} 3. Premier démarrage d'Eden Ouvrir https://IP_DU_SERVEUR:3011, accepter le certificat auto-signé. Au premier lancement, Eden propose de forcer X11 au lieu de Wayland : accepter (c'est le chemin réparé par le correctif egl-x11, et celui qu'utilise le broker). 4. Clés, firmware, jeux et mises à jour Ordre important : les clés d'abord, le firmware ensuite (Eden a besoin des clés pour installer le firmware). prod.keys : fichier décompressé, à poser dans l'appdata : /.local/share/eden/keys/prod.keys. Redémarrer Eden ensuite. Firmware : garder le ZIP tel quel, le poser dans l'appdata (visible sous /config/ dans le conteneur), puis dans Eden : Tools → Install Firmware en pointant le ZIP. Jeux : fichiers directs .nsp ou .xci (pas d'archives), dans le dossier de plateforme switch de la bibliothèque (fs_slug RomM : switch). Mises à jour et DLC : Eden n'a pas de « dossier de patchs » — ils s'installent une fois pour toutes dans la NAND virtuelle : File → Install Files to NAND, sélectionner les .nsp de mise à jour. Vérification : clic droit sur le jeu → Properties → la version doit être celle de la mise à jour. Tout persiste dans l'appdata ( .local/share/eden/nand/) et profite à tous les joueurs. Ne placez pas les mises à jour dans la bibliothèque RomM (elles seraient scannées comme une fausse plateforme). 5. ⚠️ Réglages Eden (à faire dans le fichier, conteneur arrêté) L'interface ne sauvegarde rien quand Eden est lancé par le broker (§1.3). Éditez donc directement /.config/eden/qt-config.ini, conteneur arrêté. Chez Eden, chaque clé est accompagnée d'une ligne clé\default= : tant qu'elle vaut true, la valeur est ignorée au profit du défaut compilé. Il faut donc toujours passer \default à false. ⚠️ Après toute édition depuis l'hôte ( sed -i, cat >, redirection…), rétablissez le propriétaire : chown -R 99:100 . Ces commandes recréent le fichier et peuvent le laisser en root:root ; Eden ne peut alors plus enregistrer sa configuration, ce qui ne se voit que par un Config file could not be saved! dans son log. 5.1 Les deux réglages qui comptent docker stop eden F=/.config/eden/qt-config.ini # Backend graphique : Vulkan sed -i 's/^backend\\default=.*/backend\\default=false/; s/^backend=.*/backend=1/' "$F" # Précision GPU : Normal (le défaut compilé est High = beaucoup plus lent) sed -i 's/^gpu_accuracy\\default=.*/gpu_accuracy\\default=false/; s/^gpu_accuracy=.*/gpu_accuracy=0/' "$F" docker start eden grep -nE "^backend|^gpu_accuracy" "$F" gpu_accuracy est le réglage le plus rentable de tout ce guide : le défaut High force des chemins d'émulation beaucoup plus lents, pour une précision dont la quasi-totalité des jeux n'a pas besoin. Mesuré ici : +33 % de FPS en passant sur Normal. 5.2 Énumérations utiles Depuis janvier 2026, Eden a fusionné le backend de rendu et le backend de shaders dans une seule clé backend : backend Valeur 0 OpenGL (GLSL) 1 Vulkan (recommandé) 2 Null 3 OpenGL GLASM 4 OpenGL SPIR-V La clé shader_backend est obsolète : si elle traîne dans votre fichier, elle est ignorée en silence. Supprimez-la. gpu_accuracy Valeur 0 Normal (recommandé) 1 High (défaut compilé) 2 Extreme 5.3 Réglages vérifiés, à laisser tels quels use_asynchronous_gpu_emulation=true (indispensable), use_multi_core=true, use_disk_shader_cache=true, speed_limit=100. use_asynchronous_shaders : laisser à false — testé ici, l'activer dégrade légèrement les performances. 5.4 Langue française et plein écran Là encore, deux notions distinctes : la langue de la console émulée (celle que les jeux utilisent) et celle de l'interface d'Eden. docker stop eden F=/.config/eden/qt-config.ini # Langue de la console émulée (jeux) : 2 = français sed -i 's/^language_index\\default=.*/language_index\\default=false/; s/^language_index=.*/language_index=2/' "$F" # Plein écran persistant sed -i 's/^fullscreen\\default=.*/fullscreen\\default=false/; s/^fullscreen=.*/fullscreen=true/' "$F" docker start eden grep -nE "^language_index|^fullscreen" "$F" Valeurs de language_index : 0 japonais, 1 anglais US, 2 français, 3 allemand, 4 italien, 5 espagnol, 12 anglais UK, 13 français canadien. Interface d'Eden en français : la clé language de la section [UI] stocke un code de locale dont le format varie selon les builds. Le plus fiable est de la faire écrire par Eden lui-même : Lancer Eden manuellement (pas via RomM) depuis le bureau Selkies Emulation → Configure → General → Interface language → Français Fermer Eden proprement (File → Exit) — c'est ce qui déclenche l'écriture de la configuration Vérifier : grep -n "^language" /.config/eden/qt-config.ini Si la ligne language\default=true subsiste, la passer à false conteneur arrêté. Rappel : le plein écran est de toute façon déclenché par le broker (il envoie F11 trois secondes après le lancement). Le réglage ci-dessus le rend cohérent aussi pour les lancements manuels. Pour en sortir ponctuellement — par exemple pour lire le compteur de FPS — appuyer sur Échap. 6. ⚠️ Épinglage CPU — obligatoire sur Ryzen multi-CCD Voir la section correspondante de la page Dolphin pour l'explication détaillée. En résumé : sur un Ryzen à plusieurs CCD, les threads d'Eden migrant d'un groupe de cache à l'autre coûtent très cher, sans que rien ne paraisse saturer. Ce réglage n'est pas optionnel : il représente ici la moitié du chemin entre un jeu injouable et 60 FPS stables. lscpu -e=CPU,CORE,L3 # identifier les groupes de cache Puis épingler le conteneur sur un seul CCD via --cpuset-cpus (Extra Parameters) ou l'onglet CPU Pinning du template. Mesuré sur un 3900X avec Mario Kart 8 Deluxe : 40 → 60 FPS grâce à ce seul réglage. C'est aussi ce qui explique qu'une VM avec vCPU épinglés paraisse bien plus rapide que le même émulateur en conteneur non épinglé. Récapitulatif des gains mesurés (Mario Kart 8 Deluxe, 1080p) : Étape FPS Configuration initiale ~30 (voire écran noir sans les correctifs §2 de la page Dolphin) + gpu_accuracy=0 ~40 + épinglage CCD 60 stables 7. Côté RomM Ajouter l'entrée dans le bloc streaming du config.yml (adapter IP et ports) : - platform: switch host: https://IP_DU_SERVEUR:3011 broker_host: http://IP_DU_SERVEUR:8001 label: Eden Redémarrer RomM, rescanner la plateforme Switch. 8. Test final Fermer proprement toute instance d'Eden ouverte dans le bureau Selkies (le broker lance la sienne). Fiche d'un jeu Switch dans RomM → bouton Play on Eden. Le jeu doit s'afficher, avec le son. Vérifier l'accélération GPU pendant qu'un jeu tourne : docker exec -u abc eden sh -c 'for p in /proc/[0-9]*; do ls -l $p/fd 2>/dev/null | grep -q nvidia && echo "$(basename $p) $(tr "\0" " " < $p/cmdline | cut -c1-60)"; done' Le processus eden doit apparaître dans la liste. Astuce mesure : le broker met Eden en plein écran (il envoie F11 après 3 s), ce qui masque la barre d'état. Appuyez sur Échap pour en sortir et lire le FPS et le pourcentage de vitesse. 9. Dépannage rapide Symptôme Cause Solution « error while running the video core », log : Invalid device index -1 Nœud /dev/nvidia-modeset absent → Vulkan sans présentation §2.2 de la page Dolphin Liste de GPU vide dans les menus Graphics Idem §2.2 de la page Dolphin Réglages qui « ne tiennent pas » Config jamais écrite (processus tué par le broker) Éditer qt-config.ini conteneur arrêté (§5) Réglage écrit mais sans effet Ligne clé\default=true restée en place Passer \default à false (§5) ~30 FPS au lieu de 60, sans que rien ne sature gpu_accuracy sur High et/ou pas d'épinglage CPU §5.1 et §6 Erreur de clés à l'installation du firmware ou d'un jeu prod.keys absent ou mal placé §4, puis redémarrer Eden Jeu à la version 1.0.0 malgré la mise à jour Mise à jour non installée en NAND §4 — File → Install Files to NAND Jeu très lent, eden absent du test des fd Correctif egl-x11 manquant §2.1 de la page Dolphin Écran noir après une mise à jour du driver NVIDIA Version des libs egl-x11 changée sur l'hôte Mettre à jour les Host Path des montages 403 Forbidden nginx en ouvrant l'URL du flux Portail du mod : normal sans session RomM ouverte Page « Accès distant », §5 Écran noir depuis RomM, alors que le lancement réussit côté serveur Portail du mod : RomM 5.1.0 n'envoie pas le stream_token Page « Accès distant », §5 Le problème revient après chaque modification de template Le conteneur est recréé, le portail est réinstallé Script custom-cont-init.d — page « Accès distant », §5 RomM — Accès distant, publication HTTPS et isolation réseau Cette page complète les guides Dolphin et Eden. Elle explique comment rendre le streaming utilisable en dehors du réseau local, et comment limiter les risques que cela crée. ⚠️ À lire en entier avant de publier quoi que ce soit. Dans l'état actuel de RomM (5.1.0), rendre le flux accessible depuis Internet revient à exposer un bureau interactif sans authentification. La section 2 détaille exactement ce que cela implique. 1. Pourquoi ça ne fonctionne qu'en LAN par défaut Le bloc streaming du config.yml contient deux adresses par conteneur, et elles ne sont pas utilisées par le même acteur : - platform: switch host: https://192.168.10.100:3713 # ← chargé par le NAVIGATEUR du joueur broker_host: http://192.168.10.100:8011 # ← appelé par le BACKEND de RomM broker_host est un appel de serveur à serveur. RomM le contacte lui-même, en interne. Une adresse privée convient parfaitement. host est chargé par le navigateur du joueur, dans une iframe. RomM ne relaie rien : c'est le poste du joueur qui établit la connexion vidéo directement avec Selkies. Conséquence : avec une IP privée dans host, un joueur hors du réseau obtient un délai d'attente. La page RomM se charge, la session est bien réclamée côté serveur, mais l'image n'arrive jamais. host doit donc être une adresse joignable depuis le poste du joueur. Il n'existe que deux façons d'y arriver : amener le joueur dans le réseau (VPN), ou amener le flux jusqu'à lui (publication). Il n'y a pas de troisième voie. 2. ⚠️ Le trou de sécurité qu'il faut assumer Le point d'accès du flux n'a aucune authentification. RomM n'attache aucun identifiant à l'URL de l'iframe, et STREAMING_BROKER_SECRET — qui existe pourtant — ne protège que le côté broker_host, jamais le côté host. Il est facile de croire qu'on est couvert alors qu'on ne l'est pas. Ce que cela signifie concrètement une fois le sous-domaine publié : N'importe qui connaissant l'URL obtient un bureau interactif dans le conteneur, sans jamais toucher à RomM. L'obscurité ne protège pas. Chaque certificat Let's Encrypt est publié dans les journaux de Certificate Transparency, consultables publiquement. Un sous-domaine « non devinable » y apparaît en quelques minutes. Constaté ici : des robots scannaient le sous-domaine moins de 24 h après sa création. La bibliothèque est montée dans le conteneur. Même en lecture seule, elle reste intégralement copiable. /config est en écriture et contient les clés de console, le firmware et les sauvegardes. Le GPU et la bande passante sont utilisables par qui obtient le bureau. Le streaming contourne aussi le modèle de permissions de RomM : un compte en lecture seule peut réclamer une session et se retrouver sur ce bureau. Cela vaut même sur une instance purement locale. Ce que l'isolation réseau (section 4) apporte — et n'apporte pas Le cloisonnement décrit plus bas ferme le pire scénario : le rebond depuis le conteneur vers le reste du réseau local et vers l'hôte. C'est la mesure la plus rentable, et elle ne coûte rien aux joueurs. En revanche, elle ne protège ni la bibliothèque, ni les clés, ni le GPU. Elle réduit le rayon d'impact, elle ne referme pas la porte. Vos options, en toute honnêteté Option Protection Friction pour les joueurs LAN uniquement Totale Personne ne joue à distance VPN (WireGuard, Tailscale) Totale Chaque joueur installe un client Publication (cette page) Aucune sur le flux Aucune Publication + Access List NPM Réelle Identifiants à ressaisir à chaque session de navigateur L'Access List de NPM mérite d'être mentionnée mais déçoit à l'usage : Chrome bloque les invites d'authentification dans une iframe cross-origin, il faut donc visiter le sous-domaine une première fois pour saisir ses identifiants, et la mémorisation ne survit pas à la fermeture du navigateur. Le SSO (Authelia, Authentik…) ne fonctionne pas davantage dans ce contexte, pour les mêmes raisons de cookies SameSite et d'en-têtes X-Frame-Options. Une atténuation simple et gratuite Désactivez les Proxy Hosts entre les sessions. Deux clics dans NPM, et le sous-domaine ne répond plus du tout le reste du temps. La fenêtre d'exposition passe de « permanente » à « quand quelqu'un joue », sans aucune contrainte pour les joueurs. C'est temporaire Une PR est en cours côté RomM pour corriger exactement ce point : le broker émettra un jeton de session que RomM ajoutera à l'URL du flux, et un portail le validera avant d'autoriser la connexion. Suivre rommapp/romm#4173 et l'issue #3968. Cette page devra être révisée à ce moment-là. 3. Publier le flux derrière Nginx Proxy Manager Un sous-domaine par émulateur. Exemple avec eden.thymon.fr : DNS — un enregistrement CNAME eden vers votre domaine, exactement comme pour RomM. Vérifier la résolution avant de demander le certificat. Proxy Host, onglet Details : Champ Valeur Domain Names eden.thymon.fr Scheme https Forward Hostname / IP eden (nom du conteneur, voir §4) ou l'IP du serveur Forward Port 3001 Block Common Exploits activé Websockets Support ACTIVÉ 🚨 Visez impérativement le port HTTPS du conteneur (3001), jamais le port HTTP (3000). Le portail de sécurité installé par le mod n'existe que dans le bloc serveur HTTPS. Proxyfier vers le port 3000 contourne toute protection — l'erreur a été commise ici, et l'interface s'est retrouvée servie sans aucun filtre. NPM ne vérifie pas le certificat en amont, l'auto-signé du conteneur ne pose donc aucun problème. Onglet SSL : certificat Let's Encrypt, Force SSL, HTTP/2. Onglet Advanced — le flux est une connexion WebSocket de longue durée, les délais par défaut la couperaient au bout d'une minute d'inactivité : proxy_read_timeout 3600s; proxy_send_timeout 3600s; client_max_body_size 0; Puis dans config.yml, remplacer uniquement host: : host: https://eden.thymon.fr broker_host ne change pas : c'est RomM qui l'appelle, en interne. Tant qu'un seul émulateur est migré, la page RomM restera signalée « HTTPS brisé » par Chrome : elle continue de charger du contenu actif depuis les autres origines en certificat auto-signé. L'avertissement ne disparaît qu'une fois tous les flux publiés. Si le message persiste ensuite, réinitialisez les autorisations du site dans Chrome — l'option « autoriser le contenu non sécurisé » reste mémorisée. 4. Réseau Docker dédié et isolation C'est la partie qui réduit réellement les risques, et elle apporte aussi de la simplicité. 4.1 Créer le réseau docker network create --subnet 172.30.0.0/24 romm-net Placez-y RomM et tous les conteneurs d'émulation, avec une IP fixe pour chacun (le champ apparaît dans le template Unraid dès qu'un réseau personnalisé est sélectionné) : romm → 172.30.0.5 eden → 172.30.0.10 dolphin → 172.30.0.11 rpcs3 → 172.30.0.12 4.2 Supprimer tous les ports publiés Une fois les conteneurs sur le même réseau, ils se joignent par leur nom. Plus aucun port n'a besoin d'être publié sur l'hôte — ni les ports Selkies, ni surtout les ports des brokers, qui commandent le lancement des jeux et n'ont rien à faire sur le LAN. Dans config.yml, utilisez les noms et les ports internes : broker_host: http://eden:8000 # et non http://IP:8011 Et dans NPM, Forward Hostname = eden, port 3001. 4.3 NPM doit rester sur les deux réseaux NPM doit rejoindre romm-net pour résoudre les noms, tout en gardant son accès au LAN pour proxyfier vos autres services. On l'attache donc aux deux : docker network connect romm-net NginxProxyManager ⚠️ Cette commande ne survit pas à une recréation du conteneur (mise à jour, modification de template). Pour la rendre permanente, ajoutez dans le champ Post Arguments du template NPM : && docker network connect romm-net NginxProxyManager Unraid l'ajoute à la fin du docker run, donc le rattachement est rejoué automatiquement à chaque recréation — exactement le seul cas où il se perdait. 4.4 Couper les conteneurs du reste du réseau Deux règles suffisent. Attention : la première ne couvre pas l'accès à l'hôte lui-même, car ce trafic passe par la chaîne INPUT et non FORWARD — d'où la seconde. Créez un User Script planifié sur At First Array Start (les règles iptables ne survivent pas à un redémarrage) : #!/bin/bash SUBNET=172.30.0.0/24 LAN=192.168.10.0/24 # adapter iptables -D DOCKER-USER -s "$SUBNET" -d "$LAN" -j DROP 2>/dev/null iptables -I DOCKER-USER -s "$SUBNET" -d "$LAN" -j DROP iptables -D INPUT -s "$SUBNET" -j DROP 2>/dev/null iptables -I INPUT -s "$SUBNET" -j DROP Les -D en amont évitent d'empiler des doublons si le script est relancé. Vérification : docker exec eden sh -c 'timeout 3 curl -sI http://IP_UNRAID >/dev/null && echo "PASSE (raté)" || echo "BLOQUE (bon)"' docker exec eden sh -c 'timeout 3 curl -sI http://AUTRE_MACHINE_LAN >/dev/null && echo "PASSE (raté)" || echo "BLOQUE (bon)"' Les deux doivent afficher BLOQUE, et le streaming doit continuer de fonctionner (RomM appelle le broker, jamais l'inverse). NPM garde son IP sur le réseau bridge pour joindre le LAN, il n'est donc pas concerné par ces règles. Si vous choisissez de le placer uniquement sur romm-net, il faudra ajouter des règles ACCEPT pour son IP avant les DROP. 5. Le portail nginx du mod Eden Les versions récentes du mod eden-romm-integration installent un portail de sécurité : nginx interroge le broker ( auth_request → /verify) et exige un stream_token avant de servir quoi que ce soit. C'est une bonne chose sur le principe — mais RomM 5.1.0 n'envoie pas encore ce jeton. Le résultat est un 403 systématique, qui se manifeste par un écran noir dans le lecteur. Le journal d'accès du conteneur est sans ambiguïté : GET /?stream_token= → 200, puis GET /websockets → 101 (ouverture manuelle) GET / → 403 (iframe de RomM) Le mod Dolphin, plus ancien, n'a pas ce portail — d'où une Switch qui ne fonctionne plus alors que la GameCube va bien. Neutraliser le portail automatiquement Le portail est réinstallé à chaque démarrage du conteneur, donc un sed manuel est à refaire sans arrêt. Le point d'accroche custom-cont-init.d s'exécute malheureusement avant que le mod ne pose le portail : il faut donc y lancer une boucle de surveillance en arrière-plan. mkdir -p /mnt/user/appdata/eden-init cat > /mnt/user/appdata/eden-init/10-gate-off.sh << 'EOF' #!/bin/bash ( while true; do if grep -qE '^[[:space:]]*auth_request /_stream_auth;' /etc/nginx/sites-available/default 2>/dev/null; then sed -i '/auth_request off/!s|^\( *auth_request .*\)|#\1|' /etc/nginx/sites-available/default nginx -s reload 2>/dev/null && echo "[gate-off] portail neutralise" fi sleep 30 done ) & EOF chmod +x /mnt/user/appdata/eden-init/10-gate-off.sh chown -R root:root /mnt/user/appdata/eden-init Puis, dans le template Eden, ajouter un chemin : Champ Valeur Container Path /custom-cont-init.d Host Path /mnt/user/appdata/eden-init Access Mode Read Only Ce dossier est volontairement placé hors de /config : ces scripts s'exécutent en root, et /config est inscriptible par l'application. Sur un conteneur exposé, un répertoire d'exécution root modifiable de l'intérieur serait une porte ouverte. C'est précisément pour cette raison que LinuxServer a sorti ces dossiers de /config. Vérification (compter 40 secondes après le démarrage) : docker logs eden 2>&1 | grep gate-off docker exec eden grep -n "auth_request /_stream_auth" /etc/nginx/sites-available/default Vous devez voir « portail neutralise » et la ligne commentée. 🔻 À supprimer le jour où la PR v2 arrive. Une fois que RomM enverra le jeton, ce portail deviendra une vraie protection — et ce script la désactiverait silencieusement. 6. Pièges à connaître nginx met en cache la résolution des noms. NPM résout eden au chargement de sa configuration et garde l'IP. Après une recréation de conteneur, il tape sur l'ancienne adresse → 502 Bad Gateway, alors qu'un curl depuis NPM fonctionne (résolution fraîche). Remède : redémarrer NPM. Les IP fixes évitent le problème. Toute modification d'un template recrée le conteneur : le portail revient, la couche inscriptible est effacée (paquets installés à la main et journaux nginx perdus), et les patchs du mod sont réappliqués. Un simple redémarrage, lui, conserve tout. Le journal d'accès nginx est dans le conteneur, à /var/log/nginx/access.log — pas sous /config. C'est l'outil de diagnostic le plus fiable : il montre l'URI exacte reçue et son code de retour. Vérifier une exposition depuis l'extérieur : utilisez le journal d'accès ou un téléphone en 4G, en navigation privée. Un navigateur qui possède déjà un cookie de session vous laissera passer alors que tout le monde est bloqué. 7. Récapitulatif de l'architecture cible Un sous-domaine par émulateur, en HTTPS Let's Encrypt, WebSockets activés, pointant vers le port 3001 du conteneur Tous les conteneurs sur un réseau Docker dédié avec IP fixes, aucun port publié Brokers joignables uniquement depuis ce réseau, par nom de conteneur NPM attaché aux deux réseaux, rattachement rendu permanent par Post Arguments Conteneurs coupés du LAN et de l'hôte par deux règles iptables persistées Portail Eden neutralisé automatiquement, en attendant la v2 Et l'acceptation assumée que le flux reste accessible sans authentification tant que la v2 n'est pas là