Skip to content

Drop dev dependencies from bot docker image #35

Description

@AJaccP

🧠 Context

The bot image is about 570 MB and a large chunk of that is dev tooling the running bot never uses. The build in Dockerfile runs:

RUN uv sync --frozen --no-install-project

with no --no-dev, so it installs the whole [dependency-groups] dev set from pyproject.tomlpytest, pytest-asyncio, pytest-cov, ruff, black, pre-commit, psycopg2-binary — into what should be a runtime-only image. The bot imports and runs with only the runtime dependencies, so none of that is needed at container run time.

There's a second, connected problem. The image CMD (and the compose bot service's command:) both launch the bot with uv run python -m src.apps.discord_bot. uv run re-syncs against the lockfile at container startup — which re-installs the full dev group again (so --no-dev at build time alone gets undone at boot) and makes the container need PyPI access just to start. So the fix has to change both how the image is built and how it's launched.

Files this ticket owns:

  • Dockerfile
  • docker-compose.yml (the bot service's command:)

🛠 Implementation Plan

1. Build without dev dependencies

Add --no-dev to the build sync so only runtime dependencies land in the image:

RUN uv sync --frozen --no-install-project --no-dev

Keep --no-install-project — this is a run-from-source project (no [build-system]), so the project itself isn't installed as a package; the bot runs from the COPYed src/ via -m.

2. Launch the bot directly from the venv, not through uv run

Change the image CMD from uv run python -m ... to run the venv's Python directly:

CMD [".venv/bin/python", "-m", "src.apps.discord_bot"]

This bypasses the startup re-sync entirely: the container boots deterministically and offline, using exactly the dependencies baked in at build time. Without this change, --no-dev from step 1 is pointless — uv run would re-add the dev group on every boot.

3. Fix the compose override too

The bot service in docker-compose.yml sets command: uv run python -m src.apps.discord_bot, which overrides the image CMD. So changing only the Dockerfile leaves the compose-run bot still going through uv run and re-syncing at boot. Either update that command: to match the direct-venv form, or drop the line so the service inherits the fixed CMD from the image. Dropping it is cleaner — one definition instead of two that can drift.

4. Verify the change

Size — build before and after and compare. The absolute size drifts with the base-image version, so measure your own before/after rather than aiming at a fixed number:

docker build -t cs-bot:before .          # current Dockerfile, before your edits
# ...apply the changes...
docker build -t cs-bot:after .
docker images cs-bot                      # compare the SIZE column

A build on current main measures roughly 570 MB before and 475 MB after (~95 MB smaller).

Dev tools are gone — this should raise ModuleNotFoundError:

docker run --rm cs-bot:after .venv/bin/python -c "import pytest"

The bot still launches — bring it up with your real .env and confirm it connects (the discord_ready log line / the bot showing online):

docker compose --profile bot up bot

📝 Notes

  • .dockerignore already keeps tests/, data/, .venv/, .git/, and .env out of the build context — no change needed there.

✅ Acceptance Criteria

  • The image builds with uv sync --frozen --no-install-project --no-dev; the dev tools (pytest/ruff/black/pre-commit/psycopg2-binary) are no longer in the image.
  • The image CMD and the compose bot command: no longer use uv run — the bot launches via .venv/bin/python -m src.apps.discord_bot (compose either matches or inherits the CMD).
  • The container starts without a startup dependency sync and without needing network access to PyPI, and the bot connects to Discord as before (docker compose --profile bot up works).
  • The image is meaningfully smaller — roughly ~475 MB vs ~570 MB on a current build (~95 MB), though the absolute size drifts with the base image.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions