Every serious Minecraft server eventually needs a bridge to its community, and that bridge is almost always Discord. Public bots cover the basics, but the moment you want a custom status command, a live player counter, an RCON console you can trust, or alerts when the server crashes, you hit their limits. Building your own bot sounds intimidating. It is not. With Python and the discord.py library you can have a working, custom Minecraft Discord bot in a single evening.
This guide is written to help server owners, administrators, and plugin developers ship a bot they fully control. It is built from the official discord.py documentation, the mcstatus and mcrcon libraries, and real deployments running on the same Linux boxes that host Minecraft. By the end you will have working code for status checks, RCON commands, slash commands, and alerts, plus a secure deployment pattern. If you already run the bots from our [Best Discord Bots for Minecraft Server Management] roundup, this is the natural next step.
Why Build a Custom Bot Instead of Using a Public One?
- Full control. No paywalls, no premium tiers, no feature removed because the developer moved on.
- Custom commands. Expose exactly the data your staff need, in exactly the format you want.
- Automation. Trigger alerts, backups, and restarts from your own logic.
- Privacy. Your player data and RCON credentials never leave your server.
The trade-off is maintenance. You own the code, so you own the updates. For that reason a custom bot is best for servers that have outgrown off-the-shelf tools. Discord is also central to retention, as we explain in [How to Use Discord to Double Your Player Retention], which makes a polished bot a growth asset rather than a toy.
What Your Bot Will Do
- Report live server status: online players, max players, latency, and version.
- Run read-only or staff-only commands through RCON.
- Respond to slash commands such as
/statusand/players. - Post alerts when the server goes offline.
Prerequisites
- Python 3.11 or newer installed on the machine hosting the bot.
- A Discord account and a server where you have Manage Server permission.
- A Minecraft server with RCON enabled (in
server.properties:enable-rcon=true, a strongrcon.password, andrcon.port=25575).
Step 1: Create the Discord Application and Bot
- Go to the Discord Developer Portal and create a New Application.
- Open the Bot tab and click Add Bot, then copy the token. Treat this token like a password.
- Under Privileged Gateway Intents, enable only what you need. For this bot the default intents are enough.
- Use the OAuth2 URL Generator, scope
botandapplications.commands, and invite it to your server.
Step 2: Set Up the Python Project
mkdir mc-discord-bot && cd mc-discord-bot
python -m venv .venv
source .venv/bin/activate # Windows: .venv/Scripts/activate
pip install discord.py mcstatus mcrcon python-dotenv
Store secrets in a .env file and never commit it to Git:
DISCORD_TOKEN=your-bot-token
MC_ADDRESS=play.example.com
RCON_HOST=127.0.0.1
RCON_PORT=25575
RCON_PASSWORD=your-rcon-password
References: discord.py documentation | mcstatus | mcrcon.
Step 3: The Bot Skeleton with Slash Commands
import os
import discord
from discord import app_commands
from mcstatus import JavaServer
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.environ["DISCORD_TOKEN"]
SERVER = os.environ.get("MC_ADDRESS", "play.example.com")
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"Logged in as {client.user}")
@tree.command(name="status", description="Show Minecraft server status")
async def status(interaction: discord.Interaction):
server = JavaServer.lookup(SERVER)
try:
s = server.status()
embed = discord.Embed(title="Server Status", color=0x57F287)
embed.add_field(name="Players", value=f"{s.players.online}/{s.players.max}")
embed.add_field(name="Latency", value=f"{s.latency:.0f} ms")
embed.add_field(name="Version", value=s.version.name)
await interaction.response.send_message(embed=embed)
except Exception:
await interaction.response.send_message("Server appears offline.", ephemeral=True)
client.run(TOKEN)
Run it with python bot.py and type /status in your server. Note that mcstatus reads the public server-list ping, so it works even for servers you do not host yourself.
Step 4: Add RCON for Real Server Commands
Status is read-only. To run commands you need RCON, a protocol that lets a trusted client execute console commands. Keep RCON bound to localhost and never expose port 25575 to the internet.
from mcrcon import MCRcon
import os
def run_rcon(command: str) -> str:
with MCRcon(os.environ["RCON_HOST"], os.environ["RCON_PASSWORD"],
port=int(os.environ.get("RCON_PORT", 25575))) as mcr:
return mcr.command(command)
@tree.command(name="players", description="List online players")
async def players(interaction: discord.Interaction):
response = run_rcon("list")
await interaction.response.send_message(f"```{response}```", ephemeral=True)
Gate dangerous commands behind a Discord role check so random members cannot run /op. Use RCON read commands for public slash commands and restrict write commands to a staff role.
Step 5: Send Alerts When the Server Goes Down
Add a background task that pings the server every minute and posts to a channel on failure. This turns your bot into a lightweight uptime monitor without a third-party service.
from discord.ext import tasks
ALERT_CHANNEL_ID = 123456789012345678 # replace with your channel id
@tasks.loop(seconds=60)
async def watch_server():
server = JavaServer.lookup(SERVER)
channel = client.get_channel(ALERT_CHANNEL_ID)
try:
await server.async_status()
except Exception:
if channel:
await channel.send("Warning: the Minecraft server appears to be offline!")
@client.event
async def on_ready():
await tree.sync()
if not watch_server.is_running():
watch_server.start()
For per-player join and leave alerts, the cleanest path is a plugin such as a webhook sender that POSTs events to your bot’s HTTP endpoint. That approach scales far better than parsing RCON console logs.
Step 6: Deploy the Bot Safely
- Run the bot as a systemd service on Linux so it restarts automatically after crashes.
- Or containerize it with Docker and mount your
.envas a secret. - Never store the token in your repository. Use environment variables or a secrets manager.
- Log to a file and rotate it, so you can debug without scrolling a live terminal.
If you are also exposing data to the web, the same discipline applies as in [Minecraft Server Networking: Port Forwarding and Tunnels Guide]: close ports you do not use, and prefer outbound connections over open inbound ones.
Pros and Cons of a Custom Discord Bot
- Pros: Unlimited customization, no subscription, deep integration with your own plugins and databases, and total control over your community data.
- Cons: You maintain the code and host it 24/7, security mistakes can be costly, and Discord API changes occasionally break things.
Common Mistakes to Avoid
- Leaking the token. A public token means anyone can hijack your bot. Rotate it immediately if it ever appears in a screenshot or repo.
- Exposing RCON to the internet. Bind it to
127.0.0.1and connect locally. - Using the wrong intents. Requesting privileged intents you do not need invites security review and slower approval.
- Blocking the event loop. Never call a synchronous RCON command directly in a hot path; use async libraries or a threadpool.
- Forgetting rate limits. Discord will throttle a bot that spams messages. Batch and cache where possible.
Frequently Asked Questions
Do I need to know Python well to build a Minecraft Discord bot?
No. Basic Python plus the examples above are enough to ship a useful bot. You will grow into async patterns as you add features.
Is discord.py still maintained?
Yes. The library is actively maintained and documented at discordpy.readthedocs.io.
How can I get live player data without RCON?
Use mcstatus for the public ping, or have a plugin push player events to your bot over HTTP. Both avoid exposing the console.
Can the same bot manage multiple servers?
Yes. Store a dictionary of servers and let the user pick one, then run the same status and RCON logic against each. This pairs well with a network of proxies described in [Scaling Your Crossplay Server].
Conclusion: Own Your Community’s Front Door
A custom Discord bot is one of the highest-value, lowest-cost tools a growing Minecraft server can build. In one evening you get status commands, RCON control, and uptime alerts, all under your control. The code above is a complete starting point; extend it with economy lookups, vote rewards, and staff audit logs as your community grows.
Your next step: create the application, drop the token into a .env file, and run the skeleton. Then connect your analytics by reading [Using Plan (Player Analytics) to Grow Your Player Base] and turn your bot into a reporting dashboard.
Bonus: Add a Staff-Only RCON Command with Role Checks
Public commands should stay read-only. For anything that changes the world, gate the command behind a Discord role. This example only lets members with a configured role run a whitelist command.
import os
from mcrcon import MCRcon
STAFF_ROLE_ID = 123456789012345678 # replace with your staff role id
@tree.command(name="whitelist", description="Add a player to the whitelist")
async def whitelist(interaction: discord.Interaction, player: str):
if not any(role.id == STAFF_ROLE_ID for role in interaction.user.roles):
await interaction.response.send_message("Staff only.", ephemeral=True)
return
with MCRcon(os.environ["RCON_HOST"], os.environ["RCON_PASSWORD"],
port=int(os.environ.get("RCON_PORT", 25575))) as mcr:
result = mcr.command(f"whitelist add {player}")
await interaction.response.send_message(f"Result: {result}", ephemeral=True)
Validate every user-supplied argument before it reaches RCON. Passing raw input into a console command is a command-injection risk waiting to happen.
Storing Configuration for Multiple Servers
Once you run more than one server, hard-coding addresses gets messy. Move your server list into a small JSON file so staff can switch targets from a command instead of editing code.
import json
def load_servers():
with open("servers.json") as fh:
return json.load(fh)
# servers.json example:
# { "survival": {"address": "survival.example.com", "rcon_port": 25575},
# "creative": {"address": "creative.example.com", "rcon_port": 25575} }
Combine this with a per-server RCON password stored in environment variables, and the same bot can safely manage an entire network. That pattern is the backbone of the proxy setups covered in [Minecraft Server Networking].
Is it safe to run RCON from a Discord bot?
Yes, if you bind RCON to localhost, keep the password secret, validate input, and restrict write commands to a staff role. Never expose the RCON port publicly.

Leave a Reply