← Blogue

Comment héberger un bot Discord 24 heures sur 24

Railyard

3 juin 2026 · 11 min de lecture

Suivez ça sur votre propre serveur. Commencer gratuitement, sans carte

Le bot fonctionne. node index.js sur votre ordinateur le connecte à votre serveur de test, !ping répond pong, tout va bien, jusqu’à ce que vous fermiez le terminal ou mettiez l’ordinateur en veille, et le bot disparaît avec lui. Ce qui manque, ce n’est pas plus de code. C’est un processus qui continue de tourner une fois que vous êtes parti, ce qui est un problème différent de celui que vous avez résolu en écrivant le bot lui-même.

Ce guide construit un vrai bot discord.js, l’empaquette dans un Dockerfile, et explique la chose qui déroute presque tout le monde la première fois qu’on héberge un bot Discord : pourquoi il lui faut un processus qui ne s’arrête jamais, pas une fonction qui démarre à la demande. Ensuite on couvre la politique de redémarrage, les logs, et le chemin en un clic pour faire tourner la même chose sur Railyard.

Ce qu’il vous faut

  • Node.js 22 ou 24 installé en local (discord.js 14 demande Node 18 ou plus récent).
  • Un compte Discord et un serveur (guild) où tester, idéalement un que vous contrôlez.
  • Docker installé en local, pour construire et lancer l’image avant qu’elle n’aille ailleurs.
  • Un serveur pour faire tourner le conteneur, ou un compte Railyard pour le raccourci de la dernière section.

Étape 1 : créer l’application du bot et obtenir un token

Allez sur le portail développeur Discord et cliquez sur New Application. Donnez-lui un nom, puis ouvrez l’onglet Bot à gauche. Cliquez sur Reset Token pour générer un token, et copiez-le quelque part en sécurité. C’est l’identifiant que votre code utilise pour se connecter en tant que bot ; quiconque le possède peut contrôler votre bot, donc il va dans une variable d’environnement, jamais dans un fichier que vous committez.

Sur le même onglet Bot, sous Privileged Gateway Intents, activez Message Content Intent si votre bot doit lire le texte des messages (un bot de modération ou un bot à commandes textuelles en a besoin ; un bot qui ne répond qu’à des slash commands et des boutons n’en a pas besoin).

Ouvrez OAuth2 → URL Generator, cochez le scope bot, cochez les permissions dont votre bot a vraiment besoin (commencez par Send Messages et Read Message History pour un bot simple), copiez l’URL générée, et ouvrez-la dans un navigateur pour inviter le bot sur votre serveur de test.

Étape 2 : écrire le bot

discord.js 14.27.0 est la version npm actuelle. Initialisez le projet et installez-le :

npm init -y
npm install discord.js@^14.27.0

Ajoutez "type": "module" à package.json pour pouvoir utiliser import, et un script start :

{
  "name": "my-discord-bot",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "discord.js": "^14.27.0"
  }
}

index.js :

import { Client, GatewayIntentBits, Events } from "discord.js";

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
  ],
});

client.once(Events.ClientReady, (readyClient) => {
  console.log(`Logged in as ${readyClient.user.tag}`);
});

client.on(Events.MessageCreate, async (message) => {
  if (message.author.bot) return;
  if (message.content === "!ping") {
    await message.reply("pong");
  }
});

client.on(Events.Error, (error) => {
  console.error("Discord client error:", error);
});

client.login(process.env.DISCORD_TOKEN);

Chaque entrée de GatewayIntentBits déclare une catégorie d’événements que Discord enverra réellement à votre bot ; GuildMessages et MessageContent ensemble sont ce qui permet à MessageCreate de se déclencher avec un message.content lisible pour une commande texte simple comme !ping. Demander MessageContent dans le code sans activer le Message Content Intent dans le portail développeur à l’Étape 1 vous donne une erreur « disallowed intents » à la connexion, qui est de loin l’erreur la plus fréquente pour un nouveau bot.

Testez-le en local :

DISCORD_TOKEN=votre-token-ici node index.js

Vous devriez voir Logged in as yourbot#0000 dans le terminal, et !ping sur votre serveur de test devrait recevoir une réponse pong.

Étape 3 : écrire le Dockerfile

FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

FROM node:24-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER node
CMD ["node", "index.js"]

C’est la même forme en deux étapes que vous utiliseriez pour n’importe quelle application Node : les dépendances s’installent dans leur propre étape pour être mises en cache séparément du reste du code, et l’image finale tourne en tant qu’utilisateur non root node plutôt qu’en root. La vraie différence avec le Dockerfile d’une application web, c’est ce qui manque : pas de EXPOSE, parce que ce processus n’écoute jamais sur un port. Il ouvre une connexion sortante vers la gateway de Discord ; rien ne se connecte à lui.

Étape 4 : pourquoi ça doit être un processus toujours actif, pas une fonction

Une fonction serverless (une fonction Vercel, une Lambda AWS) démarre quand une requête HTTP arrive et s’arrête peu après avoir répondu. Ce modèle convient à une charge faite de requêtes discrètes et indépendantes. Un bot Discord n’en est pas une : une fois que client.login() réussit, discord.js ouvre une connexion WebSocket vers la gateway de Discord et la garde ouverte indéfiniment. Chaque message, réaction et interaction de slash command que votre bot voit arrive comme un événement poussé sur cette connexion déjà ouverte, pas comme une requête HTTP entrante que votre plateforme route vers un handler.

Il n’y a pas de requête pour déclencher un cold start au départ, et même si une plateforme invoquait votre code selon un autre horaire, la connexion à la gateway elle-même devrait quand même exister continuellement entre les invocations pour recevoir quoi que ce soit ; une connexion qui n’existe que pendant qu’une fonction est en train de s’exécuter n’est pas une connexion capable de recevoir un événement qui arrive entre deux invocations. C’est la vraie distinction que cherche une recherche « héberger un bot Discord » : pas la performance, mais si quelque chose écoute au moment où Discord décide d’envoyer quelque chose.

(Discord propose aussi un point de terminaison HTTP basé sur les interactions spécifiquement pour les slash commands, où Discord envoie à votre bot un POST HTTP par interaction au lieu d’un événement de gateway. Ce modèle peut tourner sur une fonction serverless, mais il ne couvre que les slash commands et les composants, pas les messages ordinaires, la présence, ni tout le reste que transporte la gateway, et c’est une architecture différente de l’approche Client plus gateway utilisée dans ce guide.)

Ce dont un bot comme celui-ci a vraiment besoin est sans éclat : un processus qui démarre, reste actif, et se reconnecte de lui-même si la connexion tombe, ce que discord.js fait déjà en interne pour les accrocs réseau ordinaires. Votre travail est de vous assurer que le processus lui-même continue d’exister, ce qui est une politique de redémarrage, pas un framework.

Étape 5 : politique de redémarrage et logs

Lancez l’image en local avec une politique de redémarrage pour que Docker la relance après un crash ou un redémarrage de la machine :

docker build -t my-discord-bot .
docker run -d --name my-discord-bot --restart unless-stopped \
  -e DISCORD_TOKEN=votre-token-ici \
  my-discord-bot

--restart unless-stopped dit au démon Docker de redémarrer le conteneur automatiquement sauf si vous l’avez explicitement arrêté vous-même, ce qui couvre à la fois une exception non gérée qui fait planter le processus et un redémarrage de la machine hôte.

Pour les logs, n’ajoutez pas encore de bibliothèque de journalisation ; console.log et console.error vers stdout/stderr suffisent pour un bot de cette taille, et Docker capture déjà les deux :

docker logs -f my-discord-bot

Vous devriez voir la ligne Logged in as... de l’Étape 2, et toute sortie console.error si le client rencontre une erreur. Continuez à journaliser les événements spécifiques à Discord qui comptent pour déboguer un bot devenu silencieux, en particulier les reconnexions, pour qu’un tableau de bord ou un terminal que vous consultez plus tard vous dise que quelque chose s’est passé plutôt que de simplement se taire :

client.on(Events.ShardDisconnect, () => console.warn("Shard disconnected, reconnecting..."));
client.on(Events.ShardReconnecting, () => console.log("Shard reconnecting..."));

Étape 6 : le faire tourner sur un vrai serveur

Copiez l’image (ou le dépôt, si vous construisez sur le serveur) vers un VPS avec Docker installé, et lancez la même commande docker run de l’Étape 5 là-bas, avec un vrai fichier .env plutôt qu’un token en ligne :

docker run -d --name my-discord-bot --restart unless-stopped --env-file .env my-discord-bot

Ce seul drapeau, --restart unless-stopped, est l’essentiel de ce que « héberger un bot 24 heures sur 24 » signifie vraiment en pratique : un processus qui revient de lui-même après tout sauf vous l’arrêter explicitement.

Le raccourci : avec Railyard

Pointez Railyard vers le même dépôt. Comme le dépôt a un Dockerfile qui copie vraiment l’application dans l’image (pas une image de développement qui attend un montage lié, que la détection de build de Railyard ignore automatiquement), ce Dockerfile se construit et tourne directement, sans que Railpack ou un buildpack n’ait à deviner quoi que ce soit ; voir les méthodes de build.

Ce bot n’a pas de processus web, et c’est normal : Railyard fait tourner les applications uniquement-worker comme n’importe quelle autre, comme un type de processus sans port attaché, couvert dans Workers et tâches planifiées. Ajoutez DISCORD_TOKEN sous l’onglet Variables de l’application (voir Variables et secrets) ; c’est chiffré et appliqué au prochain déploiement, l’équivalent côté Railyard du --env-file .env de l’Étape 6.

Un réglage à laisser tranquille : Sleep when idle, sous Settings → General, arrête un processus une fois que son CPU rapporté a été inactif assez longtemps. Un bot assis dans un serveur calme peut sembler inactif selon cette mesure alors même que sa connexion à la gateway est bien vivante et sur le point de recevoir un message ; laissez ce réglage désactivé pour ce processus, pour que la connexion ne soit jamais ce qu’on met en veille.

La politique de redémarrage de l’Étape 5 n’est pas non plus quelque chose que vous configurez ici, c’est juste la façon dont Railyard fait tourner les conteneurs de chaque application : un crash redémarre le processus sans que vous ayez à vous connecter en SSH pour taper docker start. Les logs se trouvent dans l’onglet Logs de la page de l’application, ou via railyard logs my-bot --follow depuis la CLI, la même habitude que docker logs -f de l’Étape 5 sans avoir besoin d’un terminal ouvert sur le serveur lui-même.

Pour déployer sans même coller une URL de dépôt à chaque fois, ajoutez un bouton de déploiement au README du bot :

[![Deploy to Railyard](https://app.railyard.run/deploy/badge)](https://app.railyard.run/deploy?repo=https://github.com/you/your-discord-bot)

Voir la documentation du bouton de déploiement pour le format complet du lien, et le démarrage rapide si vous partez de zéro : inscription, un serveur, et une application qui tourne en environ dix minutes.

Dépannage

Le bot apparaît hors ligne sur Discord même si le conteneur tourne. Vérifiez d’abord docker logs (ou l’onglet Logs) pour une erreur de connexion ; un token invalide ou révoqué échoue immédiatement. Si la connexion réussit mais que le bot ne répond toujours pas, confirmez que l’URL d’invitation de l’Étape 1 incluait bien le scope bot et les permissions dont vos commandes ont besoin.

« Used disallowed intents » au démarrage. Vous demandez GatewayIntentBits.MessageContent (ou un autre intent privilégié) dans le code sans l’avoir activé dans le portail développeur. Retournez à l’onglet Bot de l’Étape 1 et activez le Privileged Gateway Intent correspondant.

Le conteneur redémarre en boucle. C’est --restart unless-stopped qui fait exactement son travail face à un processus qui plante immédiatement, généralement une erreur de démarrage, pas transitoire. Vérifiez les premières lignes de docker logs juste après un redémarrage, pas la fin ; la raison du crash s’y trouve presque toujours, souvent un DISCORD_TOKEN manquant ou une faute de frappe dans un import de dépendance.

Le bot devient silencieux un moment, puis revient. C’est généralement une reconnexion à la gateway, que discord.js gère de lui-même ; la journalisation ShardDisconnect/ShardReconnecting de l’Étape 5 transforme « le bot semblait hors service » en une ligne de log que vous pouvez vraiment vérifier contre la page de statut de Discord si ça se reproduit.

FAQ

Peut-on héberger un bot Discord gratuitement ? Le framework du bot lui-même est gratuit dans tous les cas ; ce qui coûte de l’argent, c’est le calcul toujours actif sur lequel il tourne. Un petit VPS coûte quelques dollars par mois, ce qui est le vrai plancher pour « 24/7 », puisque rien de gratuit ne garde un processus vivant indéfiniment sans que vous ayez à le surveiller.

Ai-je besoin d’un serveur web pour mon bot Discord ? Non, pas pour l’approche basée sur la gateway utilisée dans ce guide. Le bot ne fait que des connexions sortantes (vers la gateway et l’API REST de Discord) ; il n’a jamais besoin d’accepter des requêtes HTTP entrantes, donc il n’y a aucun port à exposer ni de proxy inverse ou de certificat TLS à gérer pour le bot lui-même.

Qu’arrive-t-il aux messages envoyés pendant que mon bot est hors ligne ? Ils sont perdus du point de vue du bot. La gateway de Discord ne met pas en file d’attente les événements pour qu’un client déconnecté les rejoue plus tard ; si le bot était hors ligne quand un message a été envoyé, il ne voit jamais cet événement MessageCreate, ce qui est la raison pratique pour laquelle « 24/7 » compte plus pour un bot que pour la plupart des applications web.

discord.js ou discord.py : est-ce que ça change quelque chose pour l’hébergement ? Pas pour quoi que ce soit dans ce guide. Un bot discord.py (Python) a besoin des mêmes choses : un processus toujours actif, pas une fonction ; une politique de redémarrage ; et intents.message_content = True défini dans le code en plus du Message Content Intent activé dans le portail développeur, l’équivalent Python de la configuration des intents de l’Étape 2. Le Dockerfile remplace node:24-alpine par python:3.13-slim et bot.run(TOKEN) remplace client.login(...), mais l’architecture est identique.

Pourquoi ne pas simplement laisser un terminal ouvert avec node index.js qui tourne ? Ça fonctionne jusqu’à ce que le terminal se ferme, que l’ordinateur se mette en veille, ou que la session SSH se coupe, et le processus meurt avec, sans rien pour le ramener. --restart unless-stopped (ou Railyard qui fait tourner le conteneur de la même façon par défaut) fait toute la différence entre un bot en ligne parce que vous n’avez pas encore fermé votre ordinateur, et un bot vraiment en ligne.

Un bot Discord est un petit bout de code qui fait un gros travail continu : rester connecté. Une fois que le processus lui-même redémarre tout seul et journalise ce que vous pouvez voir, le fait que le bot soit toujours en ligne cesse d’être quelque chose auquel vous devez penser du tout.

Pourquoi les gens choisissent Railyard pour ça

  • Votre propre compte infonuagique. Déployez sur DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou n'importe quel serveur Linux par SSH, facturé directement par votre fournisseur, sans majoration.
  • Un prix fixe par équipe. L'offre gratuite est gratuite pour toujours : un serveur plus un serveur de base de données, trois applications, sans carte. Pro coûte 19 $ par mois pour jusqu'à cinq serveurs et un nombre illimité d'applications. Team coûte 49 $ par mois avec des serveurs illimités. Aucuns frais par application, par déploiement, par siège ou par gigaoctet.
  • Des gabarits en un clic. Déployez des applications open source, comme des bases de données, des outils d'analyse et des outils de développement, sur votre propre serveur en un clic.
  • Des bases de données gérées. Postgres, MySQL et Redis, avec des sauvegardes planifiées, vérifiées par restauration, et la récupération à un point dans le temps.
  • Déploiement par push. Déployez depuis GitHub, GitLab ou la CLI, avec HTTPS automatique et des mises en production sans interruption.

Évitez la configuration du serveur

Déployez le Dockerfile de votre bot discord.js sur Railyard comme processus worker, avec une politique de redémarrage et des logs déjà configurés.

Offre gratuite, sans carte, et 14 jours de Pro pour chaque nouvelle équipe. Prix fixe par équipe.