Guide

How much RAM does a Discord bot need?

Less than you would guess for most bots, and far more than you would guess for a few. The difference is almost never the number of commands. It is what the bot keeps in memory while it waits.

6 minute read

Rough numbers

These are ballparks for discord.py on 64-bit Linux. Measure yours rather than trusting them; the next section shows how.

  • A small command or moderation bot in a handful of servers: roughly 50 to 120 MB. Most of that is Python, discord.py and aiohttp being loaded.
  • The same bot in a few hundred servers with the members intent: a few hundred MB, mostly member and user objects.
  • Music bots: each voice connection that is playing runs an ffmpeg process, often another 20 to 50 MB per active stream.
  • Bots that process images: depends entirely on the images. A decoded 4000 × 3000 photo is about 46 MB in memory before you do anything to it.
  • Anything with machine learning libraries: importing PyTorch alone can use more than a gigabyte. Plan for that separately.

If your bot is in the first group, 512 MB is plenty and 1 GB is comfortable headroom.

Measure it

The number that matters is resident memory (RSS): what the process actually holds. From inside the bot:

import os, psutil  # add psutil to requirements.txt

@bot.command()
@commands.is_owner()
async def mem(ctx):
    rss = psutil.Process(os.getpid()).memory_info().rss
    await ctx.send(f"{rss / 1024 / 1024:.0f} MB, {len(bot.guilds)} servers, {len(bot.users)} users cached")

Check it right after starting, again after a day, and again after a busy period. A number that settles is fine. A number that only ever goes up is a leak.

On a host with a control panel, the memory graph shows the same thing without code.

What makes it grow

The member cache

With the members intent on, discord.py downloads the member list of every server at startup ("chunking") and keeps it. For big servers that is the largest single cost. If you do not need every member cached:

intents = discord.Intents.default()
intents.members = False          # if you do not need member events

bot = commands.Bot(
    command_prefix="!",
    intents=intents,
    chunk_guilds_at_startup=False,  # members still arrive as they are seen
    member_cache_flags=discord.MemberCacheFlags.from_intents(intents),
)

You can still look people up on demand with await guild.fetch_member(id), which costs a request instead of memory.

The message cache

discord.py keeps the last 1,000 messages it has seen so it can give you the old version in on_message_edit and on_message_delete. If you do not use those events:

bot = commands.Bot(..., max_messages=None)

Or set a smaller number, like 200, if you use them lightly.

Presences

The presences intent sends every status change and game activity in every server you are in. It is expensive in traffic and memory, and few bots genuinely need it. Leave it off unless you do.

Your own data

The leaks are almost always here: a dictionary of user IDs to something, added to on every message and never cleared. A cooldown tracker, an XP buffer, a "last seen" map. Give every in-memory collection a limit or an expiry, or keep it in SQLite instead of a dict.

from collections import OrderedDict

class LRU(OrderedDict):
    def __init__(self, limit=10_000):
        super().__init__()
        self.limit = limit
    def __setitem__(self, key, value):
        super().__setitem__(key, value)
        self.move_to_end(key)
        if len(self) > self.limit:
            self.popitem(last=False)

Or use functools.lru_cache for function results.

Images and files

Process images one at a time and close them. With Pillow, use with Image.open(...) as im: and shrink big images early with im.thumbnail(). Stream downloads to disk instead of reading a whole file into memory.

What happens when it runs out

On your own machine, a bot that uses too much memory slows everything down and then gets killed by the OS. On a host with a hard limit it gets killed as soon as it crosses the line, usually with exit code 137, and restarted. A bot that is killed repeatedly at the same point is telling you its real size.

Picking a size

Take the highest number you measured over a busy day, and add half again for headroom. If you cannot measure yet, 1 GB covers nearly every small and medium bot. That is what SnowServers' $3 Flurry plan gives you, with a memory graph in the panel so you can check, and a crash message that tells you plainly when memory was the reason.