DEV Community

John Wick
John Wick

Posted on

The Proper Method of Deploying Discord Bots Professionally

How to Deploy Discord Bots Like a Professional

Writing a Discord bot with discord.py is the easy part. The part that actually separates a hobby project from something people can rely on is deployment: does it survive a crash? Does it come back online automatically? Can you actually tell when something's wrong? This guide walks through how to deploy discord bot python projects properly on Railway, Render, or Koyeb, using StayPresent to handle process supervision, health checks, and recovery.

Table of Contents

  1. Why Discord.py Deployment Is Different
  2. Choosing a Platform: Railway vs Render vs Koyeb
  3. Structuring Your Project
  4. Setting Up Crash Recovery
  5. Logging Without Chaos
  6. Health Checks and Monitoring
  7. Full Deployment Example
  8. Best Practices
  9. Common Mistakes
  10. FAQs
  11. Conclusion

Why Discord.py Deployment Is Different

A discord.py bot runs bot.run(token), which blocks forever inside a WebSocket connection to Discord's gateway. It never opens an HTTP port on its own, which means most cloud platforms — which health-check via HTTP — will treat it as an unresponsive web service unless you add something to answer that check.

That "something" is a small HTTP server running in parallel with your bot process, which is exactly what StayPresent provides.

Choosing a Platform: Railway vs Render vs Koyeb

All three platforms are commonly used for discord.py bots and share the same core requirement: a service listening on a PORT environment variable.

  • Railway auto-detects Python projects and injects $PORT.
  • Render requires the same $PORT pattern and free-tier services sleep after inactivity.
  • Koyeb similarly expects an HTTP-facing process for its health checks.

Because the underlying requirement is identical across all three, the same StayPresent setup works on any of them with no changes.

Structuring Your Project

A clean structure separates your bot's logic from the entry point that manages it:

project/
├── main.py       # StayPresent entry point
├── bot.py        # discord.py bot logic
├── requirements.txt
Enter fullscreen mode Exit fullscreen mode

bot.py contains your normal discord.py code:

import discord
from discord.ext import commands
import os

intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)

@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")

bot.run(os.environ["DISCORD_TOKEN"])
Enter fullscreen mode Exit fullscreen mode

main.py is the entry point Railway, Render, or Koyeb actually start:

import os
import staypresent

staypresent.web.json({"status": "online", "bot": "discord"})

staypresent.run(
    "bot.py",
    port=int(os.getenv("PORT", 8080)),
)
Enter fullscreen mode Exit fullscreen mode

Setting Up Crash Recovery

Discord bots crash for mundane reasons — a rate limit, a bad API response, an unhandled exception in a command handler. Rather than letting one bad command take the whole bot offline, configure automatic restarts:

staypresent.run(
    "bot.py",
    port=int(os.getenv("PORT", 8080)),
    restart_on_crash=True,
    max_restarts=5,
    restart_delay=2.0,
    restart_reset_after=60.0,
)
Enter fullscreen mode Exit fullscreen mode

If the bot crashes repeatedly within a short window, max_restarts caps the attempts so you don't end up in a silent infinite crash loop. Once max_restarts is exhausted, StayPresent exits with the bot's original exit code, letting the hosting platform's own recovery policy take over as a final safety net.

Logging Without Chaos

StayPresent keeps its own logs under a dedicated "staypresent" logger instead of touching Python's root logger, so it won't interfere with whatever logging setup your bot already has:

import logging
logging.getLogger("staypresent").setLevel(logging.INFO)
Enter fullscreen mode Exit fullscreen mode

This means you can turn StayPresent's logs up when debugging a deployment issue, and back down once things are stable, without affecting your discord.py logs at all.

Health Checks and Monitoring

StayPresent exposes /health automatically, returning {"status": "ok"}. Point your platform's health check — or an external uptime monitor — at that path rather than /, which you're free to use for a human-readable status page instead:

staypresent.web.html("templates/status.html")
Enter fullscreen mode Exit fullscreen mode

Full Deployment Example

import os
import logging
import staypresent

logging.getLogger("staypresent").setLevel(logging.INFO)

staypresent.web.json({
    "status": "online",
    "bot": "discord",
})

staypresent.run(
    "bot.py",
    port=int(os.getenv("PORT", 8080)),
    threads=8,
    restart_on_crash=True,
    max_restarts=5,
    restart_delay=2.0,
)
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Keep your Discord token in an environment variable, never hardcoded, and pass extra secrets through env= if bot.py needs anything beyond what's already in the environment.
  • Set restart_reset_after generously (60 seconds is a reasonable default) so a bot that's genuinely stable isn't penalized for one unlucky crash hours later.
  • Use bot_args for feature flags instead of environment variables when a setting only matters for that one run — pass it as a list, e.g. bot_args=["--sync-commands"].

Common Mistakes

  • Blocking on bot.run() without any restart supervision, so a single unhandled exception takes the bot offline until someone manually redeploys.
  • Running the bot's HTTP server and the bot itself as two separate Render services, doubling hosting costs for something StayPresent handles in a single process pair.
  • Ignoring the difference between exit code 0 and a crash. StayPresent treats a clean sys.exit(0) as intentional and won't restart it — useful for a bot command that deliberately shuts itself down.

FAQs

Does this work with py-cord or other discord.py forks?
Yes — StayPresent doesn't care what's inside bot.py; it just runs the script as a subprocess.

Can I run multiple bots this way?
Each staypresent.run() call manages one bot script per process. For multiple bots, run separate deployments or manage multiple subprocesses inside bot.py itself.

Is this overkill for a small hobby bot?
Not really — the entire setup is a handful of lines, and it saves you from silent downtime the first time your bot hits an unexpected error at 2 a.m.

Conclusion

Deploying a discord.py bot professionally isn't about writing more code — it's about wrapping the bot you already have with proper process supervision, health checks, and logging isolation. StayPresent handles all three in a single staypresent.run() call, so you can focus on bot features instead of babysitting deployments.

pip install staypresent[prod]
Enter fullscreen mode Exit fullscreen mode

Top comments (0)