devops

How to Run Your Discord Bot in Docker

Rank.top Team4 min read

Package a Node.js or Python bot, keep tokens out of the image, and use Compose for repeatable deployments.

What Docker changes

Docker packages your runtime and dependencies so the same image can run on your laptop and host. It does not host the bot for you. You still need a machine or container service that keeps the process running.

Start with a bot that already works locally. The examples below assume a TypeScript bot built into dist/index.js, or a Python bot started with bot.py. Adjust those paths and use runtime versions supported by your dependencies.

Build the image

This Node example installs dependencies from a committed lockfile, compiles the project, and leaves development dependencies out of the final image. It uses Node 24; check your bot library’s runtime requirement before copying it.

Dockerfile · TypeScript bot
FROM node:24-bookworm-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm prune --omit=dev

FROM node:24-bookworm-slim
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build --chown=node:node /app/package*.json ./
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
USER node
CMD ["node", "dist/index.js"]

For Python, install your pinned dependencies and run as a separate application user. Add any native packages your dependencies need; voice bots may also require FFmpeg and Opus.

Dockerfile · Python bot
FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1
WORKDIR /app
RUN useradd --create-home bot
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=bot:bot . .
USER bot
CMD ["python", "bot.py"]

Run it with Compose

Create a local .env file containing the variables your bot expects, such as DISCORD_TOKEN. Keep it out of Git and restrict access on the host.

compose.yaml
services:
bot:
build: .
init: true
restart: unless-stopped
env_file: .env
stop_grace_period: 30s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Build, start, and inspect
docker compose up -d --build
docker compose logs --tail=100 bot
docker compose ps

A Gateway-only bot does not need a published port. If it stores SQLite files or uploads locally, mount a persistent volume and make sure the application user can write to it. Back up that data separately from the image.

Handle restarts deliberately

  • Handle SIGTERM: stop accepting work, finish or requeue jobs, close the Discord client, then exit.
  • Use your library’s reconnect handling for short Discord connection failures.
  • Keep the previous image available so a failed release can be rolled back.
  • Check that only the intended process owns each shard after a deployment.
  • Test a host reboot and a restore from backup before relying on the setup.

A health check can report an unhealthy process, but plain Docker Compose does not automatically restart a container just because that check fails. The restart policy responds to process exits. Monitor the actual connection and command behavior too.

For the underlying packaging concepts, see Docker’s Node.js guide. For automated releases, continue with bot CI/CD.

keep reading

More from the blog

All articles