A Discord bot that runs on your laptop dies the moment you close the lid. To keep one online 24/7 you need an always-on host, secure token storage, and reconnection handling for the gateway. This guide covers the architecture decisions and the deploy.
Gateway bots vs HTTP interactions
There are two fundamentally different bot architectures, and they have opposite hosting requirements:
- Gateway bots maintain a persistent WebSocket connection to Discord and receive events in real time (messages, presence, voice). This needs a long-running process that never sleeps.
- HTTP interaction endpoints receive slash-command interactions as webhooks over HTTPS. Discord POSTs to your URL; you respond. This can run as a request-driven service and tolerates scale-to-zero better.
Most classic bots are gateway bots. If you only handle slash commands, the HTTP interactions model is leaner. Know which you're building before you deploy.
A minimal gateway bot (discord.js)
import { Client, GatewayIntentBits } from 'discord.js'
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages]
})
client.once('ready', () => console.log(`Logged in as ${client.user.tag}`))
client.on('interactionCreate', async (i) => {
if (i.isChatInputCommand() && i.commandName === 'ping') {
await i.reply('Pong!')
}
})
client.login(process.env.DISCORD_TOKEN)Never commit your token
The bot token is a full credential. If it leaks, attackers control your bot. Always load it from an environment variable and add a secret scanner to CI. If a token leaks, regenerate it in the Discord Developer Portal immediately.
Reconnection and reliability
discord.js handles gateway reconnects automatically, but you should still log disconnects and avoid crashing on unhandled rejections, which would otherwise drop the bot until a restart:
process.on('unhandledRejection', (err) => console.error('Unhandled:', err))
client.on('shardDisconnect', (e, id) => console.warn(`Shard ${id} disconnected`, e.code))For large bots (2,500+ guilds) Discord requires sharding; discord.js can manage shards for you. Most bots never reach that threshold.
Persistent state
If your bot remembers anything — settings, scores, reminders — store it in a database, not in memory. A restart (which happens on every deploy) wipes in-memory state.
const { rows } = await pool.query('SELECT prefix FROM guilds WHERE id=$1', [guildId])Dockerfile
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER node
CMD ["node", "index.js"]A gateway bot has no inbound HTTP port — it connects out to Discord.
Deploying on PandaStack
Gateway bot (always-on): deploy as a container app, but run it on a tier that keeps one instance alive — do *not* use free-tier scale-to-zero for a gateway bot, since the WebSocket must stay connected. Store DISCORD_TOKEN as an environment secret in the [dashboard](https://dashboard.pandastack.io). If your bot has state, attach a managed PostgreSQL database and read DATABASE_URL.
HTTP interactions bot: deploy as a container app exposing your interactions endpoint. Set the endpoint URL in the Discord Developer Portal. Because it's request-driven, it tolerates scale-to-zero, and the automatic SSL PandaStack provides satisfies Discord's HTTPS requirement.
| Bot type | Connection | Scale-to-zero OK? | PandaStack setup |
|---|---|---|---|
| Gateway | Persistent WS | No | Always-on container app |
| HTTP interactions | Inbound webhook | Yes | Container app + SSL endpoint |
Logs and monitoring
PandaStack gives you live application logs (self-hosted Elasticsearch), so you can watch reconnects and command errors in real time — invaluable when a bot misbehaves only in production.
References
- [Discord Developer Docs: Gateway](https://discord.com/developers/docs/topics/gateway)
- [Discord: Receiving and responding to interactions](https://discord.com/developers/docs/interactions/receiving-and-responding)
- [discord.js guide](https://discordjs.guide/)
- [Discord: Sharding](https://discord.com/developers/docs/topics/gateway#sharding)
Keep your bot online around the clock with live logs and a managed database on PandaStack — get started at [dashboard.pandastack.io](https://dashboard.pandastack.io).