ShinyProxy : auto-héberger une application Shiny pour toute une équipe avec Docker
Servez un conteneur isolé par utilisateur, derrière une connexion d’entreprise, sur votre propre serveur — open-source et auto-hébergé.
Auto-hébergez une application Shiny pour de nombreux utilisateurs avec ShinyProxy. Exécutez la session de chaque utilisateur dans son propre conteneur Docker derrière une connexion, écrivez un application.yml minimal, lancez le serveur, pointez-le vers l’image que vous avez construite, ajoutez une authentification d’entreprise (LDAP, OpenID Connect, SAML) et placez HTTPS en frontal avec un reverse proxy.
- ShinyProxy sert un conteneur Docker par utilisateur. Chaque visiteur obtient une session R isolée dans son propre conteneur — aucun état partagé, aucun plantage d’un utilisateur ne fait tomber la session d’un autre.
- Il gère la connexion à votre place. L’authentification est intégrée : identifiant/mot de passe pour les tests, ou nativement LDAP, OpenID Connect et SAML pour l’authentification unique (SSO) d’entreprise. Vous la configurez — vous n’écrivez jamais de code d’authentification.
- La configuration tient dans un seul fichier. Un court
application.yml(un port, un mode d’authentification, un backend de conteneur et une liste de spécifications d’applications) suffit à démarrer et à servir. - Il exécute l’image que vous avez déjà construite. ShinyProxy lance à la demande l’image de conteneur de la leçon Docker — rien ne change dans votre Dockerfile.
- The runtime is the judge. Quand ShinyProxy répond à
http://localhost:8080et lance votre application au clic, vous avez prouvé que le déploiement fonctionne.
Introduction
Votre application tourne dans un conteneur — vous avez construit l’image dans la leçon Docker. Maintenant, 20 collègues en ont besoin en même temps, chacun avec sa propre session isolée, derrière une connexion d’entreprise, sur un serveur que vous contrôlez. Faire tourner un seul processus Shiny Server partagé ne suffit pas : tout le monde partage le même processus R, donc le calcul lourd ou le plantage d’un utilisateur touche tout le monde, et il n’y a aucun contrôle d’accès par utilisateur.
ShinyProxy est la réponse open-source. À chaque connexion, il démarre un conteneur Docker neuf qui exécute votre application pour ce seul utilisateur, y relaie le navigateur, et arrête le conteneur quand l’utilisateur part. Cela vous apporte deux choses qu’un serveur ordinaire ne peut pas offrir : une isolation complète par utilisateur (un conteneur par session) et une authentification d’entreprise intégrée. Il est auto-hébergé et gratuit — vous l’exécutez sur votre propre infrastructure, donc les données privées ne quittent jamais votre réseau.
Optez pour ShinyProxy quand vous avez besoin d’un hébergement multi-utilisateurs, sur site, avec un vrai contrôle d’accès. Si vous voulez simplement une URL gérée sans serveur à administrer, déployez plutôt sur shinyapps.io. Si vous voulez une plateforme commerciale tout-en-un avec des contrats de support, Posit Connect en est l’équivalent payant. ShinyProxy est le bon choix quand vous voulez un contrôle total sans frais de licence, et que vous êtes prêt à administrer la machine.
Cette leçon prend l’image que vous avez construite et la sert à une équipe de bout en bout : comment fonctionne ShinyProxy, le application.yml, l’exécution du serveur, le pointage vers votre propre image, l’ajout d’une connexion, et la mise en place de HTTPS en frontal.
Il vous faut trois choses : un serveur avec Docker installé (ShinyProxy lance les conteneurs à travers lui) ; une application Shiny conteneurisée — si vous n’avez pas encore d’image, conteneurisez d’abord votre application ; et un moyen d’exécuter ShinyProxy lui-même — soit l’image Docker openanalytics/shinyproxy (rien d’autre à installer), soit le JAR (Java ARchive — un unique fichier .jar exécutable), indépendant de la plateforme, qui nécessite un JDK (Java Development Kit) version 21 ou plus récente (depuis ShinyProxy 3.2.0 ; installez-en un depuis Adoptium). Cette leçon utilise l’image Docker, donc Docker est le seul prérequis strict.
Les configurations et les commandes de cette page ne sont pas exécutées ici — ShinyProxy tourne sur votre serveur et crée des conteneurs Docker, ce qu’aucune page web statique ne peut faire. Copiez n’importe quel bloc sur votre machine et exécutez-le. Le vrai test, c’est le serveur en fonctionnement : quand ShinyProxy répond à http://localhost:8080 et lance une application au clic, vous avez prouvé que le déploiement fonctionne — the runtime is the judge.
Le fichier application.yml
Tout ce que fait ShinyProxy est piloté par un seul fichier de configuration, application.yml. En voici une version minimale et fonctionnelle — elle démarre le serveur et sert une application de démonstration sans connexion :
proxy:
port: 8080
authentication: none # none | simple | ldap | openid | saml
container-backend: docker # docker | docker-swarm | kubernetes | ecs
docker:
internal-networking: true # ShinyProxy runs as a container itself, so it reaches
default-container-network: sp-net # the app containers over a shared Docker network
specs:
- id: 01_hello
display-name: Hello Application
description: A demo Shiny app
container-image: openanalytics/shinyproxy-demo
container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"]Lisez-le de haut en bas :
port: 8080est le port sur lequel ShinyProxy écoute.authentication: nonedésactive la connexion — parfait pour un premier démarrage sur votre propre machine. Les autres modes (simple,ldap,openid,saml) arrivent plus bas.container-backend: dockerindique à ShinyProxy de lancer les conteneurs d’applications avec le Docker local. Il prend aussi en charge Docker Swarm, Kubernetes et Amazon ECS pour les clusters plus grands.docker:— comme ShinyProxy s’exécute lui-même comme un conteneur ici,internal-networking: trueet undefault-container-networkpartagé lui permettent d’atteindre les conteneurs d’applications qu’il lance. Vous créez ce réseau une fois (docker network create sp-net, montré ci-dessous). C’est la configuration documentée pour un ShinyProxy conteneurisé.specs:est la liste des applications à proposer. Chaque spécification a besoin au minimum d’unidet d’unecontainer-image;display-nameetdescriptionsont ce que l’utilisateur voit sur la page d’accueil.container-cmdest la commande qui démarre l’application à l’intérieur du conteneur.
L’ensemble complet des options est documenté dans la référence de configuration de ShinyProxy — les deux champs de spécification obligatoires sont id et container-image.
Exécuter ShinyProxy
Exécutez l’image officielle en montant deux choses : le socket Docker (pour que ShinyProxy puisse créer les conteneurs d’applications) et votre application.yml :
# create the shared network once, so ShinyProxy and the app containers can reach each other
docker network create sp-net
docker run -d --name shinyproxy \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $PWD/application.yml:/opt/shinyproxy/application.yml \
--net sp-net \
-p 8080:8080 openanalytics/shinyproxy:3.2.4-v /var/run/docker.sock:/var/run/docker.sock donne à ShinyProxy l’accès au démon Docker de l’hôte — c’est ce qui lui permet de démarrer un conteneur par utilisateur. -v $PWD/application.yml:/opt/shinyproxy/application.yml monte votre configuration à l’endroit où ShinyProxy la lit. --net sp-net rattache ShinyProxy au réseau partagé de la configuration, pour qu’il puisse atteindre les conteneurs d’applications qu’il lance. -p 8080:8080 expose le port. L’image openanalytics/shinyproxy est multi-architecture (fonctionne à la fois sur amd64 et arm64) ; 3.2.4 est la version actuelle (publiée le 2026-04-30) — épinglez toujours une version, jamais :latest.
Ouvrez http://localhost:8080 dans un navigateur. Vous verrez la page d’accueil des applications ; cliquez sur l’application de démonstration et ShinyProxy démarre son conteneur et vous affiche l’application. Le journal de démarrage indique Started ShinyProxy 3.2.4.
Ce serveur en fonctionnement est la preuve. Un fichier de configuration ne peut pas vous dire que le déploiement est correct ; un ShinyProxy qui répond à :8080 et lance réellement l’application, oui. C’est le principe du runtime-is-the-judge — s’il sert la démonstration ici, il servira votre application de la même façon.
Servir votre propre image d’application
La spécification de démonstration exécute un exemple fourni. Pour servir votre application, pointez une spécification vers l’image que vous avez construite dans la leçon Docker :
proxy:
port: 8080
authentication: none
container-backend: docker
docker:
internal-networking: true
default-container-network: sp-net
specs:
- id: my-app
display-name: My Dashboard
description: Our team's Shiny dashboard
container-image: my-shiny-app # the image you built and (optionally) pushed
container-cmd: ["R", "-e", "shiny::runApp('/srv/shiny-server', host = '0.0.0.0', port = 3838)"]
port: 3838Deux champs font le vrai travail et méritent d’être compris :
container-cmdest la commande que ShinyProxy exécute à l’intérieur de votre conteneur pour démarrer l’application. Pour une image construite surrocker/shiny(comme dans la leçon Docker), l’application se trouve dans/srv/shiny-server, vous la lancez donc avecshiny::runApp(...), en l’attachant à0.0.0.0(pour que ShinyProxy puisse l’atteindre) et à un port.portindique à ShinyProxy sur quel port l’application écoute à l’intérieur du conteneur ; il remplace la valeur par défaut de 3838 (le port de Shiny Server). Si votre application écoute déjà sur 3838, vous pouvez l’omettre — mais l’indiquer explicitement est plus clair.
Si votre image est sur un registre, ShinyProxy la télécharge la première fois ; assurez-vous que l’hôte qui exécute ShinyProxy peut atteindre ce registre (ou faites d’abord un docker pull de l’image sur l’hôte). Au-delà de id et container-image, vous pouvez plafonner les ressources par session — par exemple container-memory-limit: "2g" et container-cpu-limit: 1 — pour qu’aucune session d’utilisateur ne puisse affamer la machine ; voir la référence de configuration pour l’ensemble complet des champs de spécification.
Ajouter une connexion
Remplacez authentication: none par un vrai mode. Commencez par simple — une liste identifiant/mot de passe intégrée, pratique pour les tests avant de raccorder un annuaire :
proxy:
authentication: simple
users:
- name: jack
password: password
groups: [admins]Maintenant, ShinyProxy affiche une page de connexion et ne laisse entrer que jack. N’expédiez jamais simple avec des mots de passe en clair en production — c’est réservé aux tests et aux tout petits déploiements.
Pour une organisation, ShinyProxy délègue l’authentification à votre système d’identité existant nativement — vous définissez un mode et ses paramètres de connexion, et ShinyProxy gère le protocole. Vous n’écrivez pas vous-même de code de session, de jeton ou de hachage de mot de passe ; bricoler soi-même l’authentification, c’est ainsi que des failles de sécurité sont expédiées. Les trois modes d’entreprise :
ldap— authentifiez-vous contre LDAP (Lightweight Directory Access Protocol) ou AD (Active Directory), les services d’annuaire que la plupart des entreprises utilisent déjà. Définissezauthentication: ldapet les paramètres de connexionproxy.ldap.*(documentation LDAP).openid— OIDC (OpenID Connect), le standard moderne à base de jetons, pour des fournisseurs comme Keycloak, Okta, Microsoft Entra ID (Azure AD) et Google. Définissezauthentication: openidet les paramètresproxy.openid.*(documentation OpenID).saml— SAML (Security Assertion Markup Language), le standard basé sur XML derrière une grande partie du SSO (authentification unique) d’entreprise. Définissezauthentication: samlet les paramètresproxy.saml.*(documentation SAML).
Avec l’un de ces modes, ShinyProxy lit les appartenances de groupe de l’utilisateur depuis le fournisseur d’identité, et vous pouvez restreindre chaque application avec access-groups pour que seules les bonnes équipes la voient. Une leçon dédiée à l’authentification approfondit la mise en correspondance des groupes et de l’accès aux applications ; ici, le point essentiel est : choisissez le mode que votre organisation utilise déjà et déléguez-lui la tâche.
Placer HTTPS en frontal
ShinyProxy parle du HTTP simple sur le port 8080. Pour tout ce qui dépasse votre ordinateur portable, placez un reverse proxy devant lui pour gérer le TLS (Transport Layer Security — le chiffrement derrière HTTPS). Le schéma standard consiste à faire terminer le TLS par nginx ou Traefik sur les ports 80/443 et à transmettre à ShinyProxy sur le 8080 — Traefik peut même récupérer et renouveler automatiquement des certificats gratuits de Let’s Encrypt. Le reverse proxy vous donne aussi un vrai nom d’hôte (analytics.yourcompany.com) au lieu de host:8080. La pile de production complète — reverse proxy, supervision et backends de cluster — relève d’une configuration d’orchestration d’entreprise qui dépasse cette leçon ; ici, retenez que le TLS se termine au niveau du reverse proxy, pas dans ShinyProxy.
Supervision de base
ShinyProxy est bâti sur Spring Boot, il expose donc des points de terminaison opérationnels d’emblée — un contrôle de santé et des métriques d’usage que vous pouvez brancher à un moniteur. Le schéma de production courant consiste à collecter ces métriques avec Prometheus et à les représenter dans Grafana ; ShinyProxy prend aussi en charge un journal d’événements/d’usage pour suivre les démarrages et arrêts d’applications (documentation des statistiques d’usage). Mettre en place la pile complète Prometheus + Grafana est un sujet d’orchestration à part entière ; pour l’instant, sachez que ces points de terminaison existent et que ShinyProxy est conçu pour être supervisé comme n’importe quel autre service Spring Boot.
Quelle cible pour quel besoin
ShinyProxy est l’une des quatre façons courantes d’héberger une application Shiny. Faites correspondre le besoin à la cible :
| ShinyProxy (cette leçon) | shinyapps.io | Posit Connect | Shiny Server (open source) | |
|---|---|---|---|---|
| Qui l’administre | Vous, auto-hébergé | Posit (géré) | Vous, auto-hébergé | Vous, auto-hébergé |
| Coût | Gratuit (open source) | Palier gratuit, puis à l’usage | Licence commerciale | Gratuit (open source) |
| Isolation par utilisateur | ✅ Un conteneur chacun | Géré par application | ✅ Oui | ❌ Processus R partagés |
| Authentification | ✅ LDAP / OIDC / SAML intégrées | Paliers payants uniquement | ✅ Entreprise, intégrée | ❌ Basique / aucune |
| Idéal pour | Multi-utilisateurs, sur site, données privées | Démonstrations, tableaux de bord, portfolios | Organisations voulant une plateforme tout-en-un avec support | Hébergement interne simple, un seul hôte |
Choisissez ShinyProxy quand vous avez besoin de nombreux utilisateurs, d’une isolation par session, d’une connexion d’entreprise et d’un contrôle sur site sans frais de licence. Choisissez shinyapps.io pour l’hébergement géré le plus rapide. Choisissez Posit Connect quand vous voulez une plateforme commerciale avec support et contenu mixte (Shiny plus rapports et API). Le simple Shiny Server ne convient que pour une application interne simple et à faible enjeu, où l’isolation et l’authentification importent peu.
Problèmes fréquents
Le conteneur de l’application ne démarre pas. ShinyProxy télécharge sans problème l’image de démonstration, mais la spécification de votre application échoue. Le plus souvent, l’image n’est pas disponible sur l’hôte (téléchargez-la d’abord avec docker pull my-shiny-app, ou vérifiez le nom du registre), ou le container-cmd ne correspond pas à la façon dont l’image sert l’application. Testez d’abord l’image directement — docker run --rm -p 3838:3838 my-shiny-app et ouvrez http://localhost:3838; si elle ne fonctionne pas seule, elle ne fonctionnera pas sous ShinyProxy. Consultez les journaux du conteneur ShinyProxy avec docker logs shinyproxy.
Cannot connect to the Docker daemon. ShinyProxy n’arrive pas à joindre Docker, il ne peut donc pas créer de conteneurs d’applications. Vérifiez que vous avez monté le socket avec -v /var/run/docker.sock:/var/run/docker.sock, que le démon Docker tourne sur l’hôte, et que l’utilisateur qui exécute ShinyProxy a la permission d’utiliser le socket. Sous Linux, cette permission signifie généralement faire partie du groupe docker.
La connexion échoue contre LDAP ou votre fournisseur SSO. Une mauvaise configuration de l’authentification d’entreprise — un bind DN (Distinguished Name), une URL ou un secret client erroné. Activez la journalisation de débogage de Spring Security pour voir l’échec exact, et revérifiez chaque valeur de connexion auprès de votre fournisseur. Testez la connectivité LDAP de manière indépendante (par exemple avec ldapsearch) avant d’accuser ShinyProxy. Comme ShinyProxy délègue au fournisseur, la correction se trouve presque toujours dans les paramètres de connexion, pas dans ShinyProxy lui-même.
Questions fréquentes
ShinyProxy est un serveur gratuit et open-source qui auto-héberge des applications Shiny (et d’autres applications web) pour de nombreux utilisateurs. À chaque connexion, il démarre un conteneur Docker neuf qui exécute votre application pour ce seul utilisateur, y relaie le navigateur, et l’arrête quand l’utilisateur part — offrant une isolation par utilisateur ainsi qu’une authentification d’entreprise intégrée (LDAP, OpenID Connect, SAML). C’est l’étape multi-utilisateurs, sur site, qui suit la conteneurisation de votre application.
Utilisez ShinyProxy pour un hébergement gratuit, auto-hébergé, multi-utilisateurs, avec une isolation par conteneur et une connexion d’entreprise. Utilisez le simple Shiny Server (open source) uniquement pour une application interne simple, où les utilisateurs partagent les processus R et où vous n’avez pas besoin d’authentification. Utilisez Posit Connect quand vous voulez une plateforme commerciale et prise en charge, hébergeant Shiny plus des rapports et des API sur une infrastructure que vous contrôlez. ShinyProxy est le juste milieu open-source : des fonctionnalités d’entreprise, aucun frais de licence, mais c’est vous qui administrez le serveur.
Placez l’application derrière ShinyProxy et définissez authentication dans application.yml. Pour les annuaires d’entreprise, utilisez ldap (LDAP / Active Directory) ; pour l’authentification unique moderne à base de jetons, utilisez openid (OpenID Connect — Keycloak, Okta, Entra ID, Google) ; et pour le SSO basé sur SAML, utilisez saml, chacun avec ses paramètres de connexion proxy.<mode>.*. ShinyProxy gère le protocole nativement — vous configurez la connexion, vous n’écrivez jamais de code d’authentification. Voir la documentation de configuration de ShinyProxy.
Il n’y a pas de limite fixe — comme chaque session tourne dans son propre conteneur, la capacité est bornée par le CPU et la mémoire de votre serveur et par la lourdeur de chaque application. Vous dimensionnez la machine pour le nombre d’utilisateurs simultanés attendu et plafonnez chaque session avec container-memory et container-cpu-limit. Pour plus de capacité qu’une seule machine, ShinyProxy prend en charge des backends de cluster (Docker Swarm, Kubernetes) pour répartir les conteneurs sur plusieurs hôtes.
Oui. ShinyProxy est open source et gratuit à auto-héberger, y compris son authentification d’entreprise (LDAP, OpenID Connect, SAML) et son isolation d’un conteneur par utilisateur. L’entreprise qui le développe, Open Analytics, propose un support payant optionnel et une édition entreprise, mais le ShinyProxy standard que vous téléchargez et exécutez ne coûte rien. Voir la page de téléchargements.
Testez vos connaissances
Écrivez un application.yml minimal qui sert une seule application à partir de l’image my-shiny-app (construite sur rocker/shiny, elle sert donc depuis /srv/shiny-server sur le port 3838), derrière une simple connexion identifiant/mot de passe avec un seul utilisateur dana (mot de passe secret, groupe team).
Il vous faut proxy.port, proxy.authentication: simple, une liste proxy.users avec une entrée, proxy.container-backend: docker, le bloc réseau proxy.docker (internal-networking: true + default-container-network: sp-net), et une entrée sous proxy.specs avec un id, une container-image, un container-cmd qui exécute shiny::runApp('/srv/shiny-server', host = '0.0.0.0', port = 3838), et port: 3838.
proxy:
port: 8080
authentication: simple
container-backend: docker
docker:
internal-networking: true
default-container-network: sp-net
users:
- name: dana
password: secret
groups: [team]
specs:
- id: my-app
display-name: My Dashboard
container-image: my-shiny-app
container-cmd: ["R", "-e", "shiny::runApp('/srv/shiny-server', host = '0.0.0.0', port = 3838)"]
port: 3838authentication: simple active la connexion intégrée et users liste qui peut se connecter. L’unique spécification pointe vers votre image ; container-cmd démarre l’application à l’intérieur du conteneur en l’attachant à 0.0.0.0 pour que ShinyProxy puisse l’atteindre, et port: 3838 indique à ShinyProxy où l’application écoute. Exécutez-le avec la commande docker run de la leçon et ouvrez http://localhost:8080 — vous obtiendrez une page de connexion, puis l’application. (N’utilisez simple que pour les tests ; passez à ldap/openid/saml pour de vraies équipes.)
Pourquoi la commande docker run de ShinyProxy monte-t-elle /var/run/docker.sock ?
A. Pour stocker les fichiers journaux de ShinyProxy sur l’hôte.
B. Pour que ShinyProxy puisse démarrer et arrêter un conteneur Docker par utilisateur — il pilote le démon Docker de l’hôte pour créer le conteneur d’application de chaque session.
C. Pour accélérer le rendu de l’application.
D. Parce que ShinyProxy stocke les mots de passe des utilisateurs dans le socket.
B. Tout le modèle de ShinyProxy repose sur un conteneur par session d’utilisateur, et il crée ces conteneurs en dialoguant avec le démon Docker de l’hôte via son socket, à /var/run/docker.sock. Sans ce montage, ShinyProxy ne peut pas créer de conteneurs d’applications et vous obtenez Cannot connect to the Docker daemon. A est hors sujet (les journaux vont ailleurs) ; C est faux (le socket n’affecte pas la vitesse de l’application) ; D est inventé — l’authentification se configure dans application.yml, pas dans le socket.
Conclusion
ShinyProxy transforme l’image que vous avez construite en un déploiement multi-utilisateurs et auto-hébergé : il exécute un conteneur Docker par utilisateur, derrière une connexion, sur votre propre serveur. La configuration tient dans un seul application.yml — un port, un mode d’authentification, le backend Docker, et une liste de spécifications d’applications pointant vers vos images. Exécutez l’image officielle avec le socket Docker monté, ouvrez http://localhost:8080, et le serveur qui lance votre application au clic est la preuve que cela fonctionne. Commencez par authentication: none pour démarrer, ajoutez simple pour tester la connexion, puis déléguez à ldap, openid ou saml pour l’authentification unique (SSO) d’entreprise — ShinyProxy gère le protocole, donc vous n’écrivez jamais de code d’authentification. Placez un reverse proxy en frontal pour HTTPS, et vous avez un déploiement Shiny open-source, de qualité production, pour toute votre équipe.
Leçons connexes
- Construire et faire monter en charge le déploiement : conteneuriser votre application produit l’image même que ShinyProxy sert, commencez donc par là si vous n’en avez pas encore. Vous préférez une URL gérée sans serveur à administrer ? Déployer sur shinyapps.io est l’alternative hébergée. Nouveau sur Shiny ? Construire votre première application Shiny vous donne l’application à conteneuriser et à servir.
- Aller plus loin : Applications web Shiny — tout le cursus Shiny. · Science computationnelle et IA — le pilier.
Prouvez que vous savez le faire. Maîtrisez toute la série Shiny en production — suivez votre parcours, construisez des projets et obtenez un certificat.
Passez à Pro — Prova illimité sur vos propres données et un certificat vérifiable qui atteste la compétence.
dès 15 $/mois facturé annuellement
✓ Vous êtes Pro — continuez. The runtime is the judge.
Réutilisation
Citation
@online{2026,
author = {},
title = {ShinyProxy : auto-héberger une application Shiny pour toute
une équipe avec Docker},
date = {2026-07-16},
url = {https://www.datanovia.com/learn/programming/shiny/production/shinyproxy},
langid = {fr}
}
Comment fonctionne ShinyProxy
ShinyProxy est une petite application Java qui se place devant vos applications. Le déroulement pour un utilisateur est le suivant :
Ce conteneur par utilisateur est tout l’intérêt : les sessions ne peuvent pas interférer entre elles, et vous pouvez plafonner la mémoire et le CPU de chacune. Comme ShinyProxy pilote Docker directement, il a besoin d’un accès au socket Docker de l’hôte.
flowchart TD U[Navigateur de l'utilisateur] --> SP[Serveur ShinyProxy] SP --> AUTH{Connecté ?} AUTH -->|Non| LOGIN[Page de connexion] AUTH -->|Oui| D[Moteur Docker] D --> C1[Conteneur d'app : utilisateur 1] D --> C2[Conteneur d'app : utilisateur 2] D --> C3[Conteneur d'app : utilisateur N]