Skip to content

Containerize Discord botΒ #13

@AJaccP

Description

@AJaccP

🧠 Context

The Discord bot currently runs only as a local process (make discord β†’ uv run python -m src.apps.discord_bot). To host it always-on later, it needs to run in a container. This ticket containerizes the bot and adds it to docker-compose.yml behind a profile, so it's available for deployment without changing the day-to-day workflow for contributors who only need Postgres/Redis for tests.

There is already a commented-out worker service in docker-compose.yml β€” it shows the exact pattern to follow (build: ., the internal database URL, depends_on). Reference it.

This is infrastructure + docs only β€” no application code changes. src/apps/discord_bot.py is not touched.


πŸ›  Implementation Plan

  1. Dockerfile (new, repo root). Build an image that runs the bot:

    • Use uv's combined base image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim (also published as astral/uv:python3.12-bookworm-slim on Docker Hub). It bundles uv + Python 3.12 on Debian bookworm-slim, so there's no separate base image and no need to copy the uv binary in β€” this is the approach uv's own Docker guide recommends. The tag tracks the current uv for that Python/OS.
    • Install dependencies from the committed lockfile with uv sync --frozen so the image matches CI's resolution. Install dependencies before copying src/ so the dependency layer is cached across code changes.
    • Copy src/ and run the bot module as the container command.
    • Follow uv's official container guide for the layer-caching and project-install details: https://docs.astral.sh/uv/guides/integration/docker/
  2. .dockerignore (new, repo root). Keep the image small and avoid baking in secrets: exclude at least .venv, .git, tests, data, docker, __pycache__, and .env (the token must come in at runtime, never be built into the image).

  3. docker-compose.yml β€” add a bot service behind a profile.

    • build: . (the new Dockerfile), command runs the bot module.
    • profiles: ["bot"] β€” this is the key requirement. With a profile set, a plain docker compose up does not start the bot; only docker compose --profile bot up does. This keeps the default test workflow (Postgres + Redis) unchanged.
    • depends_on: [postgres]. The bot does not use Redis at runtime, so don't depend on it.
    • env_file: .env to supply the token, guild ID, models, etc., plus two environment: overrides for the values that differ inside a container (see networking note below).
  4. README.md β€” add a ## Bot Deployment section. Document: the prerequisites, the build/run commands, and the networking caveats. Keep all new content within this one new section to avoid conflicts with other contributors editing the README concurrently.

  5. (Optional) add a make discord-docker target wrapping docker compose --profile bot up to mirror the existing discord target.

Container networking (host vs internal addresses)

A container does not share the host's localhost, so two settings from .env are wrong inside the container and must be overridden in the bot service's environment::

  • Database β€” inside the compose network, Postgres is reached at postgres:5432 (the service name + internal port), not localhost:5445. The commented worker service already shows this exact URL: postgresql+asyncpg://postgres:postgres@postgres:5432/cs_assistant.
  • Ollama β€” Ollama runs on the host, not in compose, so the container reaches it via host.docker.internal:11434 (OLLAMA_URL=http://host.docker.internal:11434). On Docker Desktop (macOS/Windows) this name resolves automatically; on Linux it does not, so add extra_hosts: ["host.docker.internal:host-gateway"] to the service (harmless on macOS/Windows, so include it unconditionally).

Everything else (DISCORD_BOT_TOKEN, DISCORD_GUILD_ID, OLLAMA_CHAT_MODEL, EMBEDDING_DIM, TOP_K, …) comes from .env via env_file unchanged.

Prerequisites to document in the Deployment section

  • Postgres running, with migrations and ingestion already run from the host (make migrate + make ingest) β€” the bot container only reads the data; it does not migrate or ingest.
  • Ollama running on the host with both models pulled.
  • .env populated with a valid DISCORD_BOT_TOKEN and DISCORD_GUILD_ID (the bot exits at startup without them).

πŸ“ Notes

  • Ollama stays on the host β€” it is intentionally not containerized (it holds the models and will use a GPU host in production). The container reaches out to it.
  • No app code changes. If you find yourself editing src/apps/discord_bot.py, stop β€” that's out of scope for this ticket.
  • There are no automated tests for this ticket; it's verified by building and running the container (see below). The existing make test still needs Docker for the DB tests.
  • Keep README edits inside the new ## Deployment section.

βœ… Acceptance Criteria

  • A Dockerfile at the repo root builds an image that runs the bot, installing deps via uv sync --frozen.
  • A .dockerignore excludes .venv, .git, tests, data, and .env (at minimum).
  • docker-compose.yml has a bot service behind profiles: ["bot"]. A plain docker compose up -d does not start it (verify with docker compose ps β€” only postgres and redis come up); docker compose --profile bot up does.
  • The bot service reaches Postgres at postgres:5432 and host Ollama at host.docker.internal:11434, with extra_hosts set for Linux.
  • README.md has a ## Deployment section covering prerequisites (migrate + ingest from host, Ollama running, token in .env), the build/run commands, and the database/Ollama networking caveats including the Linux host-gateway note.
  • docker compose --profile bot up builds and starts the bot, it logs discord_ready, and /ask works in the dev server (manual check β€” needs a valid token).
  • make lint passes (the pre-commit check-yaml hook validates docker-compose.yml; trailing-whitespace / end-of-file hooks apply to the Dockerfile and README).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

Status
In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions