Host a Discord or Telegram bot on Renode
Run a Discord or Telegram bot on a RE:NODE Node.js or Python server: the start script or entry file, keeping the token out of git, surviving restarts, and why a bot needs no port.
All docs pages
A bot runs on a Node.js or Python server, and starts exactly the way those pages describe: a start script or entry file on Node.js, the entry file on the Startup tab on Python. What is different about a bot is what it does not need, and what it must not do.
What is different about a bot#
- It needs no inbound port. A Discord bot and a Telegram bot using long polling both connect *out* to the provider. Nothing has to reach the server, so there is nothing to bind. (A Telegram bot using webhooks is a web app: it must listen on
SERVER_PORTand needs an HTTPS address - see Custom domains.) - The token is a secret. Read it from the environment - a
.envuploaded through the Files tab, or a Startup tab variable. Never commit it and never log it. A token pushed to a public repository is usually found and abused within minutes. - It will be restarted. A push with restart on push on restarts it; so does any other restart. Handle reconnects, and keep anything you need across restarts in files under
/home/containeror in a database, not in memory.
A Discord bot in Node.js#
{ "name": "my-bot", "private": true, "scripts": { "start": "node bot.js" }, "dependencies": { "discord.js": "^14.16.0", "dotenv": "^16.4.0" }}require("dotenv").config();const { Client, Events, GatewayIntentBits } = require("discord.js");const token = process.env.DISCORD_TOKEN;if (!token) { console.error("DISCORD_TOKEN is not set - upload .env through the Files tab"); process.exit(1);}const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent],});client.once(Events.ClientReady, (c) => console.log(`Logged in as ${c.user.tag}`));client.on(Events.MessageCreate, (message) => { if (message.content === "!ping") message.reply("pong");});client.login(token);DISCORD_TOKEN=your-token-hereA Discord bot in Python#
discord.py==2.4.0python-dotenv==1.0.1import osimport discordfrom dotenv import load_dotenvload_dotenv()intents = discord.Intents.default()intents.message_content = Trueclient = discord.Client(intents=intents)@client.eventasync def on_ready(): print(f"Logged in as {client.user}", flush=True)@client.eventasync def on_message(message): if message.content == "!ping": await message.reply("pong")client.run(os.environ["DISCORD_TOKEN"])Set the entry file on the Startup tab to bot.py if the panel did not pick it already.
A Telegram bot in Python#
Long polling needs no port and no domain:
python-telegram-bot==21.6python-dotenv==1.0.1import osfrom dotenv import load_dotenvfrom telegram import Updatefrom telegram.ext import Application, CommandHandler, ContextTypesload_dotenv()async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text("Hello from Renode")app = Application.builder().token(os.environ["TELEGRAM_TOKEN"]).build()app.add_handler(CommandHandler("start", start))app.run_polling()Checklist#
- The bot starts from a
startscript (Node.js) or the entry file on the Startup tab (Python). - The token is read from the environment, is not committed and is never printed.
.envis in.gitignore, and uploaded through the Files tab.- State that must survive a restart is in files under
/home/containeror in a database. - The bot reconnects on its own after a disconnect.
Common errors#
The console shows the bot logging in, then nothing happens. The process is alive. Check the provider's side before the code: on Discord, the privileged intents (Message Content, Server Members) must be switched on in the Developer Portal for the bot to receive those events.
`DISCORD_TOKEN is not set`, or an invalid token error. The .env was not uploaded, is not in /home/container, or has a stray space or quote around the value.
The bot replies twice. Two copies are running - for example the same token on your own computer and on the server. Stop one.
It forgets everything after a restart. State was kept in memory. Write it to a file or a database.
Questions this does not answer? Open a ticket from the panel, or start from the overview. These pages are also available as plain text for AI assistants at /llms.txt.