How to host a discord.js bot

To host a discord.js bot, create the app in the Developer Portal, read the token from an environment variable and run index.js on an always-on machine. Also set up something that restarts the process if it crashes.

Updated 4 min read

Learning objectives

After reading this article you will be able to:

  • Build a discord.js bot that reads its token from an environment variable
  • Register a slash command and test the bot locally
  • Move the bot to a host and confirm it is online
On this page

A discord.js bot is a Node.js process that opens a connection to the Discord Gateway and reacts to events. The bot shows as online while that process is up and connected; when the process stops, the bot goes offline for everyone.

This guide takes you from an empty folder to a live bot, with complete discord.js v14 code using ES modules. If you have not created the Discord app yet, how to make your first Discord bot covers that part in more detail.

index.jsNode.jsalways onDiscordGatewayDISCORD_TOKENdeploy-commands.jsruns onceregisters /pingDiscordREST API
The bot process stays on and connected to the Gateway; registering commands is a separate script that runs once against the REST API.

What do you need before you start?#

  • A Node.js LTS release. The official discord.js guide recommends the current Active LTS of Node.js, and the official release schedule shows which version that is.
  • An app in the Discord Developer Portal, with the bot created and its token copied.
  • A test server where you are allowed to invite the bot.

The token is the bot's password. See Discord bot token: where to get it and how to store it. From here on it always comes from an environment variable called DISCORD_TOKEN.

How do you set up the project?#

Create the folder and install the library:

bash
mkdir my-bot && cd my-bot
npm init -y
npm install discord.js

In package.json, add "type": "module" so you can use import. Then create a .env file (and add .env to .gitignore):

bash
DISCORD_TOKEN=paste_the_token_here
CLIENT_ID=your_application_id
GUILD_ID=your_test_server_id

Node.js can load this file itself with the --env-file flag, no extra package needed. It has existed since version 20.6 and is stable from 24.10, according to the Node.js documentation.

How do you write the bot code?#

Create index.js. The bot below uses only the Guilds intent, which is enough for slash commands, and answers a /ping command:

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

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

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

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;

  if (interaction.commandName === "ping") {
    await interaction.reply("Pong!");
  }
});

client.login(process.env.DISCORD_TOKEN);

If the bot has to read message text, you will need to enable the privileged MessageContent intent in the Portal and in the code. That is the most common stumbling block for people moving from prefix commands, and it is covered in Discord bot intents error.

How do you register the slash command?#

Starting the bot does not create the command. You must send it to the Discord API once, and again whenever its definition changes. Create deploy-commands.js:

js
import { REST, Routes, SlashCommandBuilder } from "discord.js";

const commands = [
  new SlashCommandBuilder().setName("ping").setDescription("Replies with Pong!"),
].map((command) => command.toJSON());

const rest = new REST().setToken(process.env.DISCORD_TOKEN);

await rest.put(
  Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
  { body: commands },
);
console.log(`${commands.length} command(s) registered.`);

Registering per server is the recommended way to test. Once the commands are ready, switch the route to Routes.applicationCommands(process.env.CLIENT_ID) so they apply to every server the bot is in, as the discord.js guide explains.

For the invite to work, generate the URL under OAuth2 in the Portal with the bot and applications.commands scopes.

How do you test before hosting?#

Register the command and start the bot:

bash
node --env-file=.env deploy-commands.js
node --env-file=.env index.js

The terminal should print Online as BotName. In your server, type /ping and check that the reply arrives. If the bot is online but the command is missing, go back to the registration step. If the bot never comes online, the token is wrong or was reset.

Where should you host the bot?#

Your own computer is fine for development, but the bot goes down when the machine shuts off. To stay up, the process has to run on another machine that is always on:

  • VPS: you install Node.js, set up a supervisor such as pm2 or systemd, and handle updates and the firewall. The full walkthrough is in how to keep a Discord bot online 24/7.
  • Bot hosting platform: you upload the code and set the variables; the Node.js environment already exists. It is usually faster to set up.

To estimate your bot's memory, see how much RAM a Discord bot needs, and to compare prices, how much it costs to host a Discord bot.

What errors show up when hosting?#

  • Error [TokenInvalid]: An invalid token was provided.: the DISCORD_TOKEN variable never reached the process. Your local .env does not exist on the host unless you upload it, so set the variable in the host's panel.
  • Used disallowed intents: the code asks for a privileged intent that is not enabled in the Portal.
  • The process exits with no error: the main file is wrong, or an unhandled exception killed Node.js. Read the logs and see why your Discord bot goes offline.
  • A command appears twice: the same command was registered per server and globally. Register it through only one of the two.

How to do this on Square Cloud#

On Square Cloud, the bot runs in an isolated, always-on container, with automatic restart (on by default for dashboard deploys) when the process exits with an error. You deploy it yourself by ZIP upload in the dashboard, the CLI, GitHub or the VS Code extension.

  1. Zip the project (index.js, package.json, without node_modules) and upload it in the dashboard; at start, the platform runs npm install. The squarecloud.app config file is optional: the dashboard generates it with the runtime, memory and main file, or you write it at the project root.
  2. Pick Node.js as the runtime and index.js as the main file. Each background application, such as a bot, uses at least 256 MB from the plan's RAM pool.
  3. Set DISCORD_TOKEN in the application's environment variables. The change applies on the next restart, with no rebuild.
  4. Follow the real-time logs in the dashboard, the CLI or VS Code and confirm the Online as ... line.

Plans are paid: Hobby, at R$12.49 per month, includes 1 GB of RAM and 1 vCPU. See the details in Discord bot hosting on Square Cloud.

Bot hosting

Keep your bot online 24/7 with automatic restarts

Learn more

FAQs

Which Node.js version should I use with discord.js?

Use the current Active LTS of Node.js, as the official discord.js guide recommends. It meets the minimum discord.js v14 declares on npm and gets fixes for the longest.

Do I need the MessageContent intent for my bot to work?

Not for slash commands. The Guilds intent is enough. MessageContent is privileged and is only needed if the bot must read the text of regular messages.

Can I put the token directly in the code?

No. Anyone who sees the code can control the bot. Read the token from an environment variable and, if it leaks, generate a new one in the Developer Portal.

Why does my slash command not show up in Discord?

The command has to be registered with the Discord API, which is a separate step from starting the bot. Run the registration script and check that the bot was invited with the applications.commands scope.

Next step · Step 4 of 5How to host a Discord bot 24/7To keep a Discord bot online 24/7, run its process on an always-on machine with automatic restarts. Compare your PC, a VPS with pm2 or systemd, and bot hosting.