SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-28

Auto-héberger des agents IA OpenBot sur un VPS

Découvrez OpenBot en auto-hébergement : un conteneur et un navigateur par agent, une gateway qui contrôle chaque action et la RAM réellement nécessaire.

Ce que vous obtenez en auto-hébergeant des agents IA OpenBot

Vous auto-hébergez des agents IA OpenBot en exécutant un serveur gateway et un conteneur par bot sur du matériel que vous contrôlez. Chaque conteneur de bot contient son propre navigateur Chromium et son propre volume de workspace, avec un profil de navigateur conservé entre les sessions. Chaque action effectuée par un bot sur un ordinateur, un fichier, un serveur MCP (model context protocol) ou un composant d’interface utilisateur passe par cette gateway. Celle-ci vérifie l’action par rapport à une policy avant son exécution, puis l’enregistre. Si la boucle d’agent encapsulée par cette gateway reste un sujet peu familier, le parcours progressif apprendre les agents IA depuis zéro vous fait d’abord en écrire une vous-même, avant de confier à un bot un navigateur et vos identifiants de connexion.

OpenBot est publié par CopilotKit sous licence MIT à l’adresse github.com/CopilotKit/openbot. La première release marquée, v0.0.1, a été publiée le 17 août 2026, et le projet se décrit comme étant en alpha et en développement actif. Considérez-le comme une architecture sérieuse, mais encore sujette à des évolutions importantes.

L’aspect le plus intéressant de cette architecture est aussi le plus coûteux. Un navigateur par agent représente le coût en mémoire que la plupart des utilisateurs oublient de prévoir. Le dimensionnement vient donc avant l’installation.

Comment la gateway décide de chaque action

Le serveur API sur le port 3001 est le seul chemin vers l’ordinateur d’un bot. Avant l’exécution d’une action dans le navigateur, la gateway détermine la cible à partir d’un snapshot de page, évalue les règles de policy CEL (Common Expression Language) dans leur contexte, écrit une ligne d’audit contenant la décision, puis appelle le conteneur. Si l’exécution échoue ensuite, elle écrit une deuxième ligne. La documentation décrit clairement cette limite : l’ordinateur ne décide pas de la policy, la gateway du serveur constitue la limite d’action. Cette séparation porte un nom en dehors d’OpenBot, car la boucle, les définitions des outils, les contrôles de permission et l’état de session forment ensemble le harness autour d’un modèle, et cette gateway en constitue le volet permissions.

La policy applique un refus par défaut, et les règles de refus sont évaluées avant les règles d’autorisation. Le sens de l’échec compte davantage que la syntaxe des règles. L’absence de policy n’autorise rien, et une règle défectueuse bloque l’action, qu’il s’agisse d’une règle de refus ou d’une règle d’autorisation. Une erreur dans votre policy vous laisse donc avec un bot bloqué, plutôt qu’avec un bot libre d’agir sur vos comptes. Cette couche contrôle ce que fait un bot, et non ce qu’il lit. Une page contenant des instructions destinées à l’agent reste donc un problème distinct. Il s’agit de la même surface d’attaque par prompt injection que celle à laquelle vous vous exposez lorsque vous remettez à un agent les résultats de votre propre instance SearXNG.

La piste d’audit est stockée dans PostgreSQL et survit donc à un redémarrage. Les transferts de contrôle sont enregistrés sous la forme computer.help_requested, computer.control_taken et computer.control_released. Vous pouvez ainsi voir un bot demander l’intervention d’un humain, puis voir l’humain lui rendre le contrôle. Les secrets sont enregistrés sous forme de nombres de caractères, jamais sous forme de valeurs. Les opérations sur les fichiers enregistrent le chemin et la taille, jamais le contenu. Si vous voulez la même limite de contrôle sans navigateur derrière celle-ci, la validation des actions d’un agent IA couvre ce cas plus restreint.

Combien consomme un bot en RAM et sur le disque

Le projet publie des mesures pour un seul bot sur arm64. Ce sont les seuls chiffres de dimensionnement fournis par OpenBot. Ils décrivent un bot sur une architecture donnée. Considérez-les donc comme un point de départ, et non comme un plan de capacité.

ChartOpenBot published resource figures, one Bot on arm64 (August 2026)
The data behind this chart
[
  {
    "label": "Measured, one Bot",
    "memory_gb": 0.55,
    "disk_gb": 5.3,
    "vcpu": 0.06
  },
  {
    "label": "Documented minimum",
    "memory_gb": 2,
    "disk_gb": 8,
    "vcpu": 1
  },
  {
    "label": "Documented recommended",
    "memory_gb": 4,
    "disk_gb": 10,
    "vcpu": 2
  }
]

La mémoire maximale mesurée est de 0.55 Go pour un bot. Le minimum documenté est de 2 Go et la recommandation est de 4 Go. L’écart entre la mesure et le minimum laisse de la marge à Chromium lorsqu’il est sollicité. La mémoire utilisée par un navigateur dépend des pages ouvertes, et non du processus au repos. Le CPU au repos est presque nul, à 0.06 cœur au maximum de la plage mesurée. Le CPU n’est donc pas le principal poste à prévoir. Le disque l’est. L’image seule occupe 5.3 Go, contre un volume recommandé de 10 Go. Cette taille s’explique par la présence des binaires Firefox et WebKit de Playwright, en plus de Chromium.

Ces chiffres ne vous indiquent pas combien coûteront plusieurs bots ensemble. Le projet ne publie aucun chiffre à ce sujet. Un minimum documenté correspond à la valeur qu’un projet accepte d’afficher, et non à une valeur forcément observée sous charge. C’est aussi pourquoi le choix entre PhotoPrism et Immich dépend de leurs seuils de RAM mesurés, plutôt que de leurs valeurs publiées. Effectuez vos propres mesures. Démarrez un bot, donnez-lui une tâche réelle avec une page ouverte, puis surveillez le conteneur pendant son fonctionnement.

docker stats --no-stream
free -m

Utilisez la colonne MEM USAGE du conteneur du bot comme valeur par bot. Ajoutez ensuite la passerelle et PostgreSQL, puis multipliez cette valeur par le nombre de bots que vous prévoyez d’exécuter simultanément. Un bot inactif conserve un processus de navigateur. Le multiplicateur s’applique donc aux bots présents, et pas seulement à ceux qui travaillent. Le calcul est le même que celui utilisé pour dimensionner la RAM et le CPU d’un VPS pour un agent de programmation. La partie consacrée au navigateur est traitée dans exécuter un navigateur headless pour des agents sur un VPS.

Un détail concernant Chromium affecte les petites configurations. OpenBot lance Chromium avec --disable-dev-shm-usage. Le navigateur écrit donc dans /tmp au lieu de /dev/shm. Cela évite le plantage observé sur les hôtes disposant d’un /dev/shm de petite taille. En contrepartie, la pression se déplace vers votre système de fichiers racine. C’est une raison supplémentaire pour laquelle le disque recommandé est plus grand que l’image.

Comment auto-héberger OpenBot sur un VPS ?

Vous avez besoin de Docker, de Bun 1.3 ou d’une version plus récente, d’un projet Intelligence CopilotKit et d’une clé d’API de modèle. La documentation de développement attend également lsof, python3 et curl sur le serveur. Clonez une release marquée plutôt que main, car main d’un projet alpha change sans avertissement.

git clone --branch v0.0.1 https://github.com/CopilotKit/openbot.git
cd openbot
cp .env.example .env

Provisionnez le projet Intelligence. Ces trois commandes écrivent la clé d’exécution et le token de licence dans votre fichier d’environnement.

npx --yes copilotkit@latest login
npx --yes copilotkit@latest project select
npx --yes copilotkit@latest license --write

Générez la clé qui chiffre les identifiants stockés et placez la sortie dans .env sous la forme KEY_ENCRYPTION_KEY. Ajoutez votre OPENAI_API_KEY dans le même fichier, ou définissez BOT_PROVIDER sur anthropic ou google avec la clé correspondante.

openssl rand -base64 32

Installez et démarrez ensuite le service.

bun install
bash scripts/start.sh

scripts/start.sh démarre les services Docker, exécute les migrations de la base de données, démarre le serveur et l’application, puis vérifie leur état. À la fin, l’application répond sur le port 3010 et l’API sur le port 3001. Le script signale les conflits de ports et laisse en place un service correspondant déjà en cours d’exécution. Vous pouvez donc l’exécuter deux fois sans risque.

Vérifiez le fonctionnement depuis le serveur lui-même avant d’exposer quoi que ce soit.

curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3010
ss -ltnp | grep -E ':(3010|3001|4100|4500|5432)'

Un 200 obtenu avec la première commande signifie que l’application répond. La deuxième commande affiche les adresses auxquelles ces ports sont liés. C’est cette information qui compte sur un VPS. Une ligne contenant 127.0.0.1:3001 indique que le service est accessible uniquement depuis le serveur. Une ligne contenant 0.0.0.0:3001 signifie que toute personne pouvant acheminer du trafic vers le serveur peut y accéder.

L’image de conteneur unique

La documentation de déploiement fournit également une image unique qui contient l’application, l’API et Chromium, et qui est servie sur le port 3001.

docker build -t openbot .
docker run -p 127.0.0.1:3001:3001 --env-file .env \
  -e EMBEDDED_POSTGRES=on -v openbot-data:/var/lib/postgresql/data openbot

EMBEDDED_POSTGRES=on exécute PostgreSQL dans le conteneur et applique les migrations au démarrage. Le volume nommé conserve l’historique d’audit lors d’un nouveau déploiement. Sans ce volume, chaque reconstruction supprime cet historique. Si vous configurez DATABASE_URL pour utiliser une base de données managée, l’extension vector doit y être activée. Les services managés tels que RDS, Cloud SQL et Azure Database prennent en charge cette extension, mais aucun ne l’active automatiquement. Une migration sur une base de données managée vierge échoue donc, car le type de colonne vector n’existe pas encore.

Exécutez les migrations comme étape de release lorsque la base de données est externe.

docker run --rm --env-file .env openbot \
  sh -c "cd /app/server && bun x drizzle-kit migrate --config=drizzle.config.ts"

Cette image n’expose volontairement pas le port du navigateur. Elle n’inclut pas non plus le supervisor, car celui-ci a besoin du socket Docker, que les plateformes serverless ne fournissent pas. Sans le supervisor, tous les bots partagent un navigateur et donc le même ensemble d’identifiants de connexion. L’isolation qui justifiait l’exécution d’un conteneur par bot disparaît alors. Si vous êtes ici pour utiliser des identifiants distincts pour chaque bot, exécutez la stack Compose avec COMPUTER_SUPERVISOR_URL et SUPERVISOR_TOKEN définis, sur un hôte où vous acceptez ce compromis. Un processus qui peut communiquer avec le socket Docker peut démarrer un conteneur privilégié. En pratique, il dispose donc des privilèges root sur l’hôte. C’est une bonne raison d’installer OpenBot sur sa propre machine, dans le même esprit que l’attribution d’une VM jetable aux agents de programmation.

Pourquoi OPENBOT_SINGLE_USER est un paramètre adapté à un laptop

.env.example est fourni avec OPENBOT_SINGLE_USER=true. Ce paramètre accepte chaque requête comme celle d’un administrateur et désactive complètement l’authentification. Sur un laptop, c’est pratique, car vous êtes le seul client à pouvoir atteindre le port. Sur un VPS, la première personne qui atteint le port 3010 devient administrateur d’un système qui stocke des identifiants chiffrés et contrôle un navigateur déjà connecté à vos comptes.

Il existe deux façons correctes de l’utiliser. Conservez OPENBOT_SINGLE_USER=true, liez chaque port à 127.0.0.1 et accédez à l’application uniquement via un tunnel SSH ou une interface réseau privée.

ssh -N -L 3010:127.0.0.1:3010 -L 3001:127.0.0.1:3001 you@your-vps

L’application est alors accessible à l’adresse http://localhost:3010 dans votre propre navigateur. Elle est ainsi considérée comme un contexte sécurisé. Les cookies d’authentification et les fonctionnalités du navigateur nécessaires à l’écran interactif fonctionnent donc correctement. L’autre solution consiste à désactiver le mode mono-utilisateur et à configurer un véritable fournisseur d’identité. Google, Microsoft Entra, Okta, SAML et OIDC sont pris en charge. Quel que soit le fournisseur, vous devez également définir BETTER_AUTH_SECRET sur au moins 32 caractères, configurer BETTER_AUTH_URL avec l’URL de base publique de l’API pour les callbacks OAuth, ainsi que INITIAL_ADMIN_EMAILS et TRUSTED_ORIGINS. Les identifiants du fournisseur doivent être complets, car un fournisseur partiellement configuré empêche le démarrage au lieu de rétablir un accès ouvert. Si vous ajoutez des comptes parce que chaque membre de l’équipe veut son propre agent plutôt que son propre navigateur, OneCLI est conçu dès le départ pour ce modèle, avec un agent isolé dans un sandbox par personne et les clés des modèles conservées dans une gateway unique.

Si l’application est accessible via un nom public, placez TLS (transport layer security) devant elle. Une page servie en http:// non chiffré sur autre chose que localhost n’est pas un contexte sécurisé. Les cookies marqués Secure ne sont donc pas enregistrés et l’authentification échoue d’une manière qui ressemble à un bug d’OpenBot.

Pare-feu des ports de niveau inférieur

La note de sécurité d’OpenBot indique que les points de terminaison des services de niveau inférieur sont protégés par des jetons, qu’ils doivent rester privés et qu’il ne faut pas les utiliser pour contourner la passerelle. Les jetons constituent la deuxième protection. La première consiste à rendre le port totalement inaccessible.

L’agent-computer écoute sur le port 4100 et exige COMPUTER_TOKEN. Les points de terminaison du bot écoutent sur les ports 4200 et 4201. Le superviseur écoute sur le port 4500 de l’hôte et sur le port 4300 dans son conteneur. PostgreSQL écoute sur le port 5432. Aucun de ces ports ne doit être exposé sur une interface publique. Dans un déploiement mono-utilisateur, l’application et l’API ne doivent pas l’être non plus.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

Un piège concerne les personnes qui pensent que le pare-feu suffit. La publication d’un port de conteneur avec -p 3001:3001 amène Docker à installer une règle DNAT. Le trafic est alors traité dans le chemin FORWARD et ne passe jamais par la chaîne INPUT, à laquelle s’applique le refus par défaut d’ufw. Le port reste ouvert alors que ufw status affiche toujours Status: active. Liez le port publié à l’interface loopback directement dans le mapping, par exemple avec -p 127.0.0.1:3001:3001, ou définissez l’adresse de l’hôte dans votre fichier Compose. Vérifiez avec ss -ltnp, et non avec ufw status. Ce piège n’est pas propre à OpenBot. Effectuez donc le même contrôle pour chaque autre conteneur publié sur le serveur, notamment celui qui sert une bibliothèque Jellyfin transformée en vidéoclub des années 90.

OpenBot n’est pas une stack hors ligne

Établissez ce point avant de planifier le déploiement. OpenBot dépend d’un projet CopilotKit Intelligence, qui conserve les threads persistants et l’historique des conversations en dehors de votre serveur. Au démarrage, le serveur vérifie INTELLIGENCE_API_URL, INTELLIGENCE_GATEWAY_WS_URL, INTELLIGENCE_API_KEY et COPILOTKIT_LICENSE_TOKEN. Les quatre doivent être présents simultanément, sinon le démarrage échoue. Une offre gratuite est disponible depuis août 2026. Intelligence peut également être auto-hébergé. Un déploiement entièrement local est donc possible, mais il demande davantage de travail que ne le montre le quickstart.

Le modèle est la deuxième dépendance externe. Aucun modèle n’est fourni avec l’application. BOT_PROVIDER accepte openai, anthropic ou google. OPENAI_BASE_URL fait pointer le chemin OpenAI vers n’importe quel endpoint compatible. C’est notamment ce qui permet d’exécuter Ollama sur un VPS pour auto-héberger un LLM si vous voulez que les tokens restent sur votre propre matériel. Le contrôle d’un navigateur sollicite fortement le modèle. Testez donc un modèle local sur une tâche réelle avant de vous engager.

Exécuter un seul replica pour le moment

La gateway met en cache les snapshots de pages dans la mémoire du processus serveur. Avec deux replicas, le snapshot créé par un processus n’est pas visible par l’autre. Les actions échouent donc de manière intermittente avec des erreurs element-not-found qui semblent aléatoires. La documentation de déploiement est explicite : exécutez un seul replica et définissez à 1 le nombre maximal d’instances de votre plateforme. Cette limite pourra être supprimée lorsque la mise en cache des snapshots sera déplacée vers la base de données. En attendant, vous devez augmenter les ressources de la machine pour faire évoluer OpenBot, et non ajouter des machines. L’isolation entre les bots repose toujours sur les conteneurs propres à chaque bot, comme les sandbox d’agents auto-hébergés empêchent les erreurs d’un agent d’affecter les autres.

Modes de défaillance et symptômes observés

Le démarrage s’arrête immédiatement après le renseignement de .env. Le serveur valide la configuration avant de traiter la moindre requête. Un bloc Intelligence incomplet, l’absence de KEY_ENCRYPTION_KEY ou un fournisseur OAuth avec un identifiant client mais sans secret interrompent le démarrage au lieu de dégrader le fonctionnement silencieusement. Lisez la première erreur, corrigez ce champ, puis redémarrez.

Les migrations échouent sur une base de données managée. L’extension vector n’est pas activée par défaut. La migration utilise donc un type de colonne que PostgreSQL ne connaît pas. Connectez-vous avec un compte superuser, exécutez CREATE EXTENSION vector;, puis relancez l’étape de migration.

L’application se charge, mais la connexion n’est jamais conservée. Vous utilisez http:// en clair sur une adresse publique, ce qui ne constitue pas un contexte sécurisé. Le cookie Secure est donc rejeté. Placez TLS devant l’application ou utilisez le tunnel SSH afin que le navigateur voie localhost.

Les bots utilisent les mêmes identifiants alors que vous pensiez qu’ils seraient séparés. Le superviseur ne fonctionne pas. Il n’y a donc pas d’ordinateur dédié à chaque bot et tous utilisent le navigateur partagé. Vérifiez que COMPUTER_SUPERVISOR_URL est défini et que le superviseur peut accéder au socket Docker.

Un bot s’arrête et demande de l’aide. C’est le fonctionnement prévu. La piste d’audit enregistre computer.help_requested. Vous prenez le contrôle sur l’écran actif, et la prise en main est enregistrée par les deux parties.

FAQ

OPENBOT_SINGLE_USER peut-il rester activé pour un déploiement sur un VPS ?

Uniquement si la gateway n’est pas accessible depuis Internet. OPENBOT_SINGLE_USER=true accepte chaque requête comme provenant d’un administrateur unique, sans authentification. Toute personne capable d’ouvrir le port contrôle le déploiement, ses identifiants enregistrés et le navigateur dans lequel une session est ouverte. Cette configuration convient si tous les ports sont liés à 127.0.0.1 et si vous accédez à l’application par un tunnel SSH ou une interface réseau privée. Sur une interface publique, désactivez-la et configurez Google, Microsoft Entra, Okta ou OIDC avec BETTER_AUTH_SECRET, BETTER_AUTH_URL, INITIAL_ADMIN_EMAILS et TRUSTED_ORIGINS.

De combien de RAM un bot OpenBot a-t-il besoin ?

Les chiffres publiés par le projet pour un Bot unique sur arm64 indiquent un pic de mémoire de 0.55 Go, avec 2 Go comme minimum documenté et 4 Go recommandés. Aucun chiffre n’est publié pour plusieurs bots exécutés simultanément, car chacun utilise son propre processus Chromium. Exécutez un bot sur une tâche réelle, relevez la mémoire de son conteneur dans docker stats, ajoutez la gateway et la base de données, puis multipliez le résultat par le nombre de bots que vous prévoyez d’exécuter simultanément.

Ai-je besoin d’un compte CopilotKit pour auto-héberger OpenBot ?

Oui. OpenBot dépend d’un projet CopilotKit Intelligence pour conserver les threads et la mémoire. Le serveur refuse de démarrer si l’URL de l’API Intelligence, l’URL WebSocket de la gateway, la clé API et le jeton de licence ne sont pas tous définis. Une offre gratuite est disponible depuis août 2026. Intelligence peut aussi être auto-hébergé, ce qui permet de supprimer cette dépendance hébergée au prix d’un travail supplémentaire. Vous devez également fournir votre propre clé API de modèle, car aucun modèle n’est livré avec OpenBot.

Pourquoi chaque bot utilise-t-il son propre navigateur au lieu de partager le même ?

Parce qu’un profil de navigateur constitue une identité. Un navigateur partagé implique des cookies et des sessions partagés. Ainsi, si un bot est connecté à un compte, tous les bots le sont. Les conteneurs propres à chaque bot donnent à chaque collaborateur son propre profil et ses propres connexions. Cette isolation augmente la consommation mémoire, car un processus Chromium par bot constitue le principal poste de dimensionnement.

Quels ports OpenBot doivent être ouverts sur le pare-feu ?

Aucun port des composants internes. L’agent-computer sur 4100, les endpoints des bots sur 4200 et 4201, le supervisor sur 4500 et PostgreSQL sur 5432 doivent tous rester privés. Le projet les protège avec des jetons et vous demande de les maintenir inaccessibles dans tous les cas. Publiez uniquement les ports dont un utilisateur a besoin pour ouvrir l’application. Notez qu’un port de conteneur publié avec -p 3001:3001 reste accessible même si la règle par défaut de ufw est deny, car la règle DNAT de Docker place ce trafic dans le chemin FORWARD plutôt que dans INPUT.