Discord bot sharding: when you need it and how

Sharding splits a bot's connection to Discord into several, each looking after part of its servers. Discord makes it compulsory at 2,500 servers and it is pointless long before that. Here is what it is, when you need it, and the least work way to do it.

7 min readBeginnerUpdated

What a shard is

A bot receives events, messages, members joining, reactions, over one WebSocket connection to Discord's gateway. A shard is one such connection, responsible for some of the bot's servers. With two shards, each server's events arrive on one of the two, worked out from the server's ID: shard = (server_id >> 22) % shard_count. Direct messages always arrive on shard 0.

Shards are about the gateway only. Slash commands, sending messages and the rest of the HTTP API work the same however many shards there are.

When you need it

  • Under 2,500 servers you do not. One connection handles it, and sharding a small bot only adds connections to look after.
  • At 2,500 servers it is required. A shard may hold at most 2,500 servers. Past that, Discord refuses the connection with close code 4011 ("Sharding required") and the bot cannot log in until it shards.
  • How many: Discord recommends a number itself, roughly one shard per 1,000 servers, and the libraries ask for it when you let them choose. Leave the count to them unless you have a reason not to.

Plan for it when the bot passes about 2,000 servers, so the switch is not made in a hurry on the day it stops logging in.

One process or several

There are two ways to run shards, and both libraries offer both:

  • All shards in one process. The same code, one setting changed. All shards share one cache and one set of variables, so nothing else in your code changes. This is the right choice until one process runs out of memory or CPU, which for most bots is many thousands of servers away.
  • One process per shard (or per group of shards). Each process has its own cache and memory, so a crash takes out only its own servers, and the shards can spread across CPU cores or machines. The price is that a process only knows about its own servers: totals, like the number of servers, have to be gathered from every process.

discord.py

For all shards in one process, swap commands.Bot for commands.AutoShardedBot (or discord.Client for discord.AutoShardedClient):

Python
import os
import discord
from discord.ext import commands

class MyBot(commands.AutoShardedBot):
    def __init__(self):
        super().__init__(command_prefix="!", intents=discord.Intents.default())

    async def setup_hook(self):
        await self.tree.sync()

bot = MyBot()

@bot.event
async def on_shard_ready(shard_id: int):
    print(f"Shard {shard_id} is ready")

bot.run(os.environ["BOT_TOKEN"])

It asks Discord how many shards to use and connects them one after another. Each server's shard is guild.shard_id, bot.latencies lists every shard's latency, and len(bot.guilds) is still every server. To split across processes, start each one with shard_ids=[...] and the same shard_count=; gathering totals across them is then up to you.

discord.js

For all shards in one process, tell the client to shard itself:

JavaScript
const { Client, GatewayIntentBits } = require("discord.js");

const client = new Client({ intents: [GatewayIntentBits.Guilds], shards: "auto" });
client.login(process.env.BOT_TOKEN);

Everything else stays the same, and client.guilds.cache.size is still every server. For one process per shard, discord.js has a ShardingManager: a small launcher file that starts your bot file once per shard and restarts a shard that dies:

JavaScript
// index.js: the file that runs; bot.js is your bot as it was
const { ShardingManager } = require("discord.js");

const manager = new ShardingManager("./bot.js", { token: process.env.BOT_TOKEN });
manager.on("shardCreate", (shard) => console.log(`Started shard ${shard.id}`));
manager.spawn();

Inside a shard, client.guilds.cache holds only that shard's servers. Totals come from asking all of them:

JavaScript
const counts = await client.shard.fetchClientValues("guilds.cache.size");
const servers = counts.reduce((a, b) => a + b, 0);

For anything more involved, client.shard.broadcastEval runs a function on every shard and returns the results.

Limits worth knowing

  • Logging in is rate limited. Shards connect one at a time, about five seconds apart for most bots, so a bot with 20 shards takes a couple of minutes to be fully online. The libraries wait for you.
  • Logins per day are counted. Most bots get 1,000 connection starts a day, shared by all shards. A bot stuck in a crash loop with many shards can use them up, and then cannot log in until the count resets.
  • Memory grows with servers, not shards. The cache holds the same servers either way; one process per shard adds the cost of each extra Node.js or Python process on top. The RAM guide covers what fills a bot's memory and how to cut it.

Running a sharded bot on SnowServers

A sharded bot in one process runs like any other: upload it, and the server starts it, restarts it if it crashes and shows every shard's output in the console. With discord.js's ShardingManager, make the launcher the file that runs (index.js, or main in package.json) and the shard processes share the plan's memory. If a bot that size needs more room, a plan change keeps everything on the server. Bot plans start at 1 GB.

Press / or Ctrl K on any page to search.