DISCORD BOTS

How to Host a Discord Bot for Free

Create a working discord.js slash-command bot, register commands in a test guild, keep the token private, and deploy with a reliable startup workflow.

Featured illustration for How to Host a Discord Bot for Free

A Discord bot is a small service that reacts to events in a Discord community. Before thinking about hosting, it helps to build a bot that works locally, has the correct permissions, and can explain its own failures through safe logs. This tutorial creates a working /ping slash command with discord.js, registers the command in a private test server, and prepares the project for deployment to a compatible Node.js hosting environment.

We use an interaction-based command instead of reading every message in a channel. That keeps the example focused and avoids requiring the privileged Message Content intent for a simple ping command. The walkthrough also separates one-time command registration from the long-running bot process: a distinction that prevents several common beginner mistakes.

Plan the bot before creating files

You need a Discord account, permission to install an application in a test server, a recent supported Node.js version, npm, and a text editor. If your current hosting environment does not support the Node.js version required by the latest discord.js release, choose a compatible supported combination after consulting the library’s current documentation. Avoid copying an old tutorial’s package versions without checking their maintenance status.

Our bot does one thing: respond to /ping with Pong!. This is enough to prove authentication, command registration, gateway connection, event handling, and reply permissions. Once it works, you can extend the project with moderation or community features.

What each identifier means

Discord uses several identifiers that look similar but serve different purposes. The application ID identifies the application, the guild ID identifies your test server, and the bot token is a secret credential. Application and guild IDs are used for command registration; the token authenticates your bot. Never publish the token, share it in screenshots, or copy it into a support chat.

Step 1: Create an application in Discord

Practical illustration

Open the Discord Developer Portal, create an application, and find its bot settings. Prepare the application for installation in your test server. Use the installation settings and authorization scopes appropriate for a bot with application commands; Discord’s interface evolves, so follow the current portal labels instead of assuming every screen matches a screenshot from an older guide.

Install the bot into a server where you have the necessary permissions. Prefer a private development server until you have tested all commands. For a ping command, there is no need to grant administrator permission or enable privileged intents. Only request additional access when your bot’s documented behavior requires it.

Enable Developer Mode to copy IDs

Discord’s desktop client includes a developer option that lets you copy the guild ID. Copy the application ID from the Developer Portal and the guild ID from your test server. Keep them nearby for configuration, but do not confuse them with the token. The token should be placed only in a local environment file or secret manager.

Step 2: Create the Node.js project

Make a new project folder and initialize npm:

mkdir discord-ping-bot
cd discord-ping-bot
npm init -y
npm install discord.js dotenv

Install a currently compatible discord.js release. The commands above intentionally let npm select the current published version rather than hardcoding a version that may become outdated. Commit the generated lockfile for reproducible deployments, and check the library’s documented runtime requirement before deploying.

Replace the generated package.json with a minimal CommonJS example:

{
  "name": "discord-ping-bot",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "register": "node register.js",
    "start": "node index.js",
    "check": "node --check index.js && node --check register.js"
  },
  "dependencies": {
    "discord.js": "^14.0.0",
    "dotenv": "^16.0.0"
  }
}

Important: Treat this manifest as a structural example rather than a version lock: after npm install, keep the real dependency versions and lockfile generated by npm. If you copy the example manifest over the installed one, run an appropriate install to synchronize the lockfile before deploying. It is safer to retain npm’s installed versions and add only the scripts section.

Step 3: Configure secrets and IDs

Practical illustration

Create a .env file for local development. Replace the placeholders with values from your own application and test guild:

DISCORD_TOKEN=replace_with_your_private_bot_token
CLIENT_ID=replace_with_your_application_id
GUILD_ID=replace_with_your_test_server_id

Next create .gitignore:

node_modules/
.env
*.log

Never commit .env. For production deployment, configure these variables through the host’s startup or secrets controls. The host may inject the environment directly, in which case a physical .env file is unnecessary. Do not echo the token to check its value: verify only whether the variable is defined.

Step 4: Register a guild slash command

Guild commands usually update much faster than global commands during development and let you test in one server. Create register.js as follows:

require("dotenv").config();
const { REST, Routes, SlashCommandBuilder } = require("discord.js");

const { DISCORD_TOKEN, CLIENT_ID, GUILD_ID } = process.env;
if (!DISCORD_TOKEN || !CLIENT_ID || !GUILD_ID) {
  throw new Error("Missing DISCORD_TOKEN, CLIENT_ID, or GUILD_ID");
}

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

const rest = new REST({ version: "10" }).setToken(DISCORD_TOKEN);

async function register() {
  await rest.put(
    Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID),
    { body: commands }
  );
  console.log("Guild slash commands registered successfully");
}

register().catch(error => {
  console.error("Command registration failed:", error.message);
  process.exitCode = 1;
});

This code sends the command definition to Discord’s API. You run it when installing or updating command definitions; it does not need to run on every bot restart. The put call replaces the set of commands for the specified application and guild, so plan carefully before expanding a bot with multiple commands.

Run registration once

From the project directory, execute:

npm run register

If registration fails, inspect the application ID, guild ID, token, and installation permissions. A missing command in the client can also result from installing the wrong application or testing in the wrong guild. Avoid regenerating tokens repeatedly before understanding the actual error.

Step 5: Create the running bot process

Practical illustration

Create index.js. It connects using the bot token, listens for command interactions, and replies to the ping command:

require("dotenv").config();
const {
  Client,
  Events,
  GatewayIntentBits
} = require("discord.js");

const token = process.env.DISCORD_TOKEN;
if (!token) throw new Error("DISCORD_TOKEN is missing");

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

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

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

  try {
    await interaction.reply({ content: "Pong!" });
  } catch (error) {
    console.error("Ping command failed:", error.message);
  }
});

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

client.login(token).catch(error => {
  console.error("Discord login failed:", error.message);
  process.exitCode = 1;
});

function shutdown(signal) {
  console.log(`Received ${signal}; disconnecting bot`);
  client.destroy();
}
process.once("SIGINT", () => shutdown("SIGINT"));
process.once("SIGTERM", () => shutdown("SIGTERM"));

The Guilds intent is enough for this interaction example; privileged message-content access is not required. In a larger bot you may need additional intents, but each should have a concrete purpose and be configured consistently between the Developer Portal and your code.

Step 6: Test the bot locally

First check syntax:

npm run check

Then start the process:

npm start

Wait for the ready message, switch to your test Discord server, and type /ping. The bot should reply Pong!. If you see the command but there is no response, inspect logs for interaction failures, timing issues, or missing permissions. Slash-command interactions must be acknowledged promptly; longer operations should usually defer a reply before doing expensive work.

Why not print the whole interaction object?

Dumping an entire interaction or configuration object into a production log can expose unnecessary metadata. Log an operation name and concise error description instead. Do not log the token or full environment, and avoid collecting user data that your bot does not need.

Step 7: Prepare for hosting

A hosted Discord bot needs a compatible Node.js version, installed dependencies, the correct startup file, and environment variables. Upload your source files and npm lockfile, but not local .env credentials. In the hosting dashboard, configure DISCORD_TOKEN, CLIENT_ID, and GUILD_ID where supported. For routine operation, the startup command is npm start, which runs index.js.

If your host rebuilds dependencies during deployment, follow its package-installation workflow. The command-registration step is separate: run it through a controlled deployment command or from your local development environment when you add or change slash commands. Running it repeatedly with every restart is unnecessary and can create confusing logs.

Verify after deploying

Read the console for the ready message, then use /ping in your test server. A process marked online is not proof that Discord authentication, registered commands, and replies all work. If the hosted bot does not connect, compare runtime versions, dependency installation, configuration names, and network rules against the successful local test.

On free hosting, review current resource limits, expiration behavior, restart policies, and maintenance rules. Free access is valuable for learning, but it is not the same as a contractual uptime guarantee. Keep a working local copy of the project and a record of your dependency versions.

Troubleshooting common problems

The command does not appear

Check that npm run register completed successfully, the application is installed into the same guild as GUILD_ID, and the correct bot application is being tested. Guild command propagation is usually suited to development, but you should still allow for normal API and client behavior.

Invalid token or authentication failure

Confirm you copied the bot token rather than the application ID or public key. Do not reveal the token in terminal screenshots or support messages. If it has been exposed, reset it in the Discord Developer Portal and replace it in the host’s environment.

Missing privileged intents

The example uses only GatewayIntentBits.Guilds. If you extend the bot to read message content or track certain member events, the required intents and portal configuration may change. Do not enable every intent blindly; use only what the feature needs.

Bot stops when a terminal closes

A local terminal is not a process supervisor. Use the hosting platform’s supported startup and lifecycle controls rather than relying on a development shell remaining open. If the application repeatedly crashes, investigate the first meaningful error before enabling restart loops.

The bot is connected but commands fail

Test one command and inspect its error handler. Confirm the bot has the necessary permissions in the target channel. If your handler performs work that takes time, learn how to defer interaction responses. Keep all debugging messages free of credentials.

Make the bot maintainable

As your bot grows, separate commands into modules, validate configuration at startup, and log failures with enough context to reproduce them. Document what each intent enables and why it is needed. Consider command-specific permissions and rate-limit handling before adding moderation or mass-message behavior. Test changes in a development server before installing them into your main community.

Continue with the official Discord Developer Documentation, discord.js guide, and Volyx Documentation. The portal and APIs change over time, so always compare tutorials with the current reference documentation.

Keep learning.

Explore more in-depth articles with code, visuals, and practical walkthroughs.

Browse all articles ↗