1- FROM python:3.13-slim AS python-base
1+ # ---- python-base
2+ # Common Python runtime and environment settings shared by all stages.
3+ FROM python:3.13-alpine AS python-base
24
3- # Environment variables that should exist in all images.
45ENV PYTHONUNBUFFERED=1 \
56 PIP_DISABLE_PIP_VERSION_CHECK=1 \
67 PIP_DEFAULT_TIMEOUT=100 \
@@ -9,40 +10,59 @@ ENV PYTHONUNBUFFERED=1 \
910 POETRY_CACHE_DIR='/var/cache/pypoetry'
1011
1112
12- # poetry-base stage installs Poetry and installs prod deps
13+ # ---- poetry-base
14+ # Installs Poetry and production dependencies.
1315FROM python-base AS poetry-base
1416
1517WORKDIR /app
18+
1619RUN pip install "poetry==$POETRY_VERSION" && poetry --version
1720
21+ # Install production dependencies separately so source changes do not
22+ # invalidate the dependency cache.
1823COPY pyproject.toml poetry.lock ./
19- RUN poetry install --without=dev
24+ RUN poetry install --without=dev --no-root
2025
2126
22- # dev stage continues off poetry-base to install dev deps
23- # and have poetry available within the container.
27+ # ---- dev
28+ # Development image with Poetry and development dependencies available for
29+ # local development and testing.
2430FROM poetry-base AS dev
2531
2632ENV VIRTUAL_ENV=/app/.venv \
2733 PATH="/app/.venv/bin:$PATH"
2834
2935WORKDIR /app
30- RUN poetry install --with=dev
3136
37+ RUN poetry install --with=dev --no-root
38+
39+ EXPOSE 8080
3240ENTRYPOINT ["python" , "-m" , "uqcsbot" ]
3341
3442
35- # prod stage creates the final image for production and excludes
36- # poetry as it is unneeded on prod.
43+ # ---- prod
44+ # Final production image. Excludes Poetry, build tooling, package manager,
45+ # system shell, and runs as a non-root user. It only contains the application
46+ # and virtual environment.
3747FROM python-base AS prod
3848
39- ENV VIRTUAL_ENV=/app/.venv \
40- PATH="/app/.venv/bin:$PATH"
49+ # Create an unprivileged runtime user.
50+ RUN addgroup -S -g 65532 nonroot \
51+ && adduser -S -G nonroot -u 65532 -h /home/nonroot nonroot
4152
42- COPY --from=poetry-base /app /app
53+ COPY --from=poetry-base --chown=nonroot:nonroot /app /app
54+ COPY --chown=nonroot:nonroot ./uqcsbot /app/uqcsbot
4355
4456WORKDIR /app
45- COPY ./uqcsbot ./uqcsbot
4657
47- ENTRYPOINT ["python" , "-m" , "uqcsbot" ]
58+ # Strip installer tooling from venv, and remove the package manager.
59+ RUN /app/.venv/bin/pip uninstall -y pip setuptools wheel 2>/dev/null || true ; \
60+ rm -rf /sbin/apk /etc/apk /usr/share/apk /var/cache/apk
4861
62+ USER nonroot
63+
64+ ENV VIRTUAL_ENV=/app/.venv \
65+ PATH="/app/.venv/bin:$PATH"
66+
67+ EXPOSE 8080
68+ ENTRYPOINT ["python" , "-m" , "uqcsbot" ]
0 commit comments