Host a discord.py bot

The Python path to a live bot — deploy the discord.py starter, drop in your token, and get /ping answering, then walk through app.py and add commands of your own.

Skip the setup This guide has a one-click starter template that installs everything below onto your server.

Prefer Python? discord.py gets you to the same place as the JavaScript guide — a bot answering /ping in your server — with the same short path: deploy a starter, paste your token, start. Then we'll go through app.py line by line and add a command.

At a glance
You need a bot token from Your first Discord bot, and a Falix server running the Python application
Plan free or premium — free runs while your session timer lasts, premium runs 24/7
Time about fifteen minutes

No server yet? See Create your first app server.

1. Deploy the starter

Use the template on this page. It writes app.py, requirements.txt, .env, and a couple of helper files, overwriting only files with those names. If the server currently runs a different application, deploying switches it to Python first — that reinstalls the server and wipes its files, with a warning first. On a fresh or already-Python server, nothing else changes.

2. Paste in your token

Open the File Manager and edit .env:

DISCORD_TOKEN=YOUR_TOKEN_HERE

Replace YOUR_TOKEN_HERE with the token from the Developer Portal, save, and close. (See Environment variables & secrets.)

💡 Tip: Keeping the token in .env means it never lives inside your code and never gets committed to Git.

3. Start it

Press Start on the Console page. The first run installs discord.py and python-dotenv from requirements.txt (a short wait), then prints:

Listening as YourBot#0000

That's your success signal. In your Discord server the bot's grey dot turns green — it's now connected.

🎯 Good to know: A bot only makes outbound connections to Discord, so there's no port involved.

4. Try it

In the server you invited the bot to, type /ping. It replies with its latency. If the command isn't in the list yet, wait a couple of minutes — slash commands can take a little while to register the first time — then try again.

Want the bot in a second server? The invite URL you built in the Developer Portal isn't single-use — open it again and pick another server (any where you have Manage Server). The same running bot serves every server it's in; nothing to redeploy.

How it works

Here's the app.py the template deployed:

import os

import discord
from discord import app_commands
from dotenv import load_dotenv

load_dotenv()

intents = discord.Intents.default()
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)


@client.event
async def on_ready():
    await tree.sync()
    print(f"Listening as {client.user}", flush=True)


@tree.command(name="ping", description="Check that the bot is alive")
async def ping(interaction: discord.Interaction):
    await interaction.response.send_message(
        f"Pong! {round(client.latency * 1000)} ms"
    )


client.run(os.environ["DISCORD_TOKEN"])

The pieces:

  • load_dotenv() reads .env so os.environ["DISCORD_TOKEN"] finds your token without hard-coding it.
  • discord.Intents.default() is all a slash-command bot needs. Enable a privileged intent (members, message content) in the Developer Portal only when your code actually uses it.
  • discord.Client plus an app_commands.CommandTree — the client is the connection; the tree holds your slash commands.
  • on_ready runs once after login. tree.sync() pushes your commands to Discord (that's what makes /ping appear), and the print(..., flush=True) reports success. The flush=True matters: without it Python buffers output and your line may not show in the console until much later.
  • @tree.command(...) defines a command; the function under it runs when someone uses it, replying with interaction.response.send_message(...).
  • client.run(os.environ["DISCORD_TOKEN"]) connects with the token from .env and keeps the bot alive.

Make it yours: add a second command

Adding a command is one block. Say you want /roll. At the top of the file, add import random next to the other imports, then add the command anywhere before client.run(...):

@tree.command(name="roll", description="Roll a six-sided die")
async def roll(interaction: discord.Interaction):
    result = random.randint(1, 6)
    await interaction.response.send_message(f"🎲 You rolled a {result}")

Save and restart. Because on_ready calls tree.sync() on every start, /roll registers automatically — after the first-time delay it appears next to /ping. Every command follows this pattern: decorate a function with @tree.command(...).

Adding packages

To pull in a library — a database driver, an HTTP client, anything on PyPI — open the Packages page in your server menu, type the name into the Install package panel, and press Install. Your requirements.txt is updated for you; restart so the bot loads it. Hand-editing requirements.txt works too (it's installed on every start), but the Packages page is the easy path and flags outdated or insecure packages.

Troubleshooting

  • LoginFailure: Improper token has been passed. — the token in .env is wrong or was reset. Copy a fresh one from the Developer Portal (resetting invalidates the old) and paste it in. See Bot appears offline.
  • PrivilegedIntentsRequired — your code turned on a privileged intent that isn't enabled on the Bot page in the Developer Portal. Enable it there, or don't request it.
  • Slash commands don't show up — first-time sync takes a few minutes; also make sure you invited the bot with the applications.commands scope.
  • Your print() never appears — Python buffers output; pass flush=True (the template already does) so lines show immediately.
  • Online but misbehaving — when the bot is connected but a command errors or does nothing, look in the Console: the traceback prints there, below the Listening as … line, as soon as the handler raises. Read it, fix, and restart.

Next steps

Was this guide helpful?