DevOps

How to Set Up Docker for Your Discord Bot

Rank.top Team
August 2025

Containerizing your bot ensures consistent, reliable deploys across machines and clouds. This guide gives you production‑grade Dockerfiles for Node (discord.js) and Python (discord.py), a solid .dockerignore, Docker Compose for dev and prod, and the nuances that matter for long‑lived Gateway connections (signals, restarts, voice libs).

TL;DR: Quick Start

Production basics

  • Multi‑stage builds, non‑root user, small base images.
  • Use --init or tini to forward signals.
  • Keep secrets in env vars; never bake .env into images.

Compose in prod

  • restart: unless-stopped, init: true.
  • Store token as DISCORD_TOKEN in .env or platform secrets.

Voice bots

  • Add ffmpeg (and Opus libs) in your image.
  • Prefer region‑near hosts to reduce audio latency.

Dockerfile (Node / discord.js)

Production‑ready multi‑stage build for a TypeScript bot compiled to dist/. Installs only production deps in the final image, runs as non‑root, and uses an init for clean shutdowns.

# syntax=docker/dockerfile:1
FROM node:20-slim AS base
WORKDIR /app

# Install tiny init to handle PID 1 signals (or use --init at runtime)
RUN apt-get update && apt-get install -y --no-install-recommends dumb-init && rm -rf /var/lib/apt/lists/*

FROM base AS deps
COPY package*.json ./
RUN npm ci

FROM deps AS build
COPY . .
# If using TypeScript
RUN npm run build

FROM base AS runner
ENV NODE_ENV=production
USER node
WORKDIR /app

# Copy only what we need
COPY --chown=node:node package*.json ./
RUN npm ci --omit=dev
COPY --from=build --chown=node:node /app/dist ./dist

# Uncomment if your bot needs voice (FFmpeg)
# RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "dist/index.js"]

Tip: Use node:20-alpine to reduce size, but Debian slim is simpler when you need FFmpeg.

Dockerfile (Python / discord.py)

Slim, non‑root image with optional voice packages. Adjust requirements.txt as needed (e.g., discord.py or discord.py[voice]).

# syntax=docker/dockerfile:1
FROM python:3.12-slim AS base
WORKDIR /app

# Install tiny init and (optionally) FFmpeg for voice
RUN apt-get update && apt-get install -y --no-install-recommends dumb-init && rm -rf /var/lib/apt/lists/*

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# If you need voice: add ffmpeg and opus runtime
# RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg libopus0 && rm -rf /var/lib/apt/lists/*

# Run as non-root
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["python", "bot.py"]

.dockerignore

Keep images lean and protect secrets. Place this in your project root:

node_modules
.next
dist
.venv
.env
.env.*
*.log
.git
.github
Dockerfile*
docker-compose*.yml

Never ADD/COPY your .env into the image; load it at runtime.

Docker Compose (dev & prod)

compose.dev.yml

version: "3.8"
services:
bot:
build: .
command: npm run dev # or python bot.py for Python
env_file: [.env]
environment:
- NODE_ENV=development
volumes:
- .:/app
- /app/node_modules
init: true
restart: unless-stopped

compose.prod.yml

version: "3.8"
services:
bot:
image: your-dockerhub-username/discord-bot:latest
env_file: [.env]
environment:
- NODE_ENV=production
init: true
restart: unless-stopped

Build & run: docker compose -f compose.prod.yml up -d --build

Signals, Restarts, and Health

Graceful shutdown

  • Run with --init or include dumb-init/tini in the image.
  • Ensure your bot listens for SIGTERM to disconnect and save state.

Health & restarts

Most bots don't expose HTTP. Prefer restart: unless-stopped and rely on your app's own reconnect logic for Discord Gateway. If you add a small status HTTP endpoint, you can wire a Docker HEALTHCHECK.

# Example (optional) if you add an HTTP /health endpoint on port 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -f http://localhost:3000/health || exit 1

Voice Dependencies

Node / discord.js voice

  • Add ffmpeg in the image. Many setups also use @discordjs/voice + @discordjs/opus.
  • If native modules build from source, install build-essential temporarily in a build stage.
# In runner stage (Debian slim)
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/*

Python / discord.py voice

  • Install ffmpeg and libopus0 in the image.
  • Use discord.py[voice] to pull the right extras.
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg libopus0 && rm -rf /var/lib/apt/lists/*

Deploy Targets & Practical Tips

Single VPS + Compose

  • Great for small–medium bots. Use docker compose up -d and journalctl/docker logs for ops.
  • Back up volume data (if any) and keep images updated (docker system prune cautiously).

PaaS container hosts

Fly.io, Koyeb, Render, and Railway can run your image 24/7. Use their secrets UI for DISCORD_TOKEN, set restart policies, and ship via CI/CD.

Environment & secrets

  • Put DISCORD_TOKEN and other keys in platform secrets or .env (not committed).
  • Prefer OIDC or secret managers on cloud providers for CI/CD deploys.

Online 24/7? Grow it on Rank.top

List your bot for modern discovery, built‑in analytics, and passive vote revenue. Connect webhooks in minutes.