Python with Poetry
Best practices for Dockerfile for Python with Poetry
🐳 Annotated Dockerfile for Python with Poetry:
# Start with a slim Python base image - 3.12 is recommended for modern features and performance
FROM python:3.12-slim-bookworm AS base
# Set build arguments and environment variables
ARG POETRY_VERSION=1.8.2
ENV PYTHONFAULTHANDLER=1 \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONHASHSEED=random \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
# Poetry configuration
POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_IN_PROJECT=1 \
POETRY_HOME="/opt/poetry"
# --------------------------------------
# Stage 1: Builder - installs poetry and dependencies
# --------------------------------------
FROM base AS builder
# Install Poetry using official installer
RUN pip install --no-cache-dir "poetry==$POETRY_VERSION"
# Set working directory
WORKDIR /app
# Copy only requirements-related files to optimize caching
COPY pyproject.toml poetry.lock* ./
# Configure Poetry to use system Python
RUN poetry config virtualenvs.create false
# Install dependencies using Poetry with caching
# (Only dependencies, without development dependencies)
RUN --mount=type=cache,target=/root/.cache/pypoetry \
poetry install --only main --no-root
# Copy the rest of the application code
COPY . .
# Install the project itself
RUN --mount=type=cache,target=/root/.cache/pypoetry \
poetry install --only main
# --------------------------------------
# Stage 2: Final production image
# --------------------------------------
FROM base
# Copy the virtual environment from the builder stage
COPY --from=builder /app /app
# Set working directory
WORKDIR /app
# Set proper path to include the virtual environment
ENV PATH="/app/.venv/bin:$PATH"
# Expose the port your application runs on
EXPOSE 8000
# Run your application using the virtual environment's Python
CMD ["python", "-m", "your_package.main"]
🔍 Why these are best practices:
✅ Multi-stage builds
- Efficiently separates the build environment from the runtime environment.
- Dramatically reduces final image size by not including build tools in production.
- Improves security by minimizing the attack surface in your production container.
✅ Poetry for dependency management
- Precise, deterministic dependency resolution with lockfiles.
- Clear separation between development and production dependencies.
- Ensures identical environments across development, testing, and production.
✅ Caching Poetry dependencies
- Uses Docker's build cache effectively to avoid redundant downloads.
- Significantly speeds up build time, especially in CI/CD environments.
- Reduces network usage and dependency resolution time.
✅ Environment variable optimization
- PYTHONDONTWRITEBYTECODE=1 avoids creating .pyc files, reducing image size.
- PYTHONUNBUFFERED=1 ensures logs are output immediately, improving visibility.
- POETRY_VIRTUALENVS_IN_PROJECT=1 keeps virtual environments in the project for better portability.
✅ Minimal final container
- Smaller attack surface with fewer installed packages.
- Faster container startup and less resource usage.
- Improved security posture by excluding build tools from production.
🚀 Additional Dockerfile best practices you can adopt:
Create and use a non-root user
Enhance security by running your application as a non-privileged user:
FROM base
# Create a non-root user
RUN adduser --disabled-password --gecos "" appuser
COPY --from=builder /app /app
WORKDIR /app
ENV PATH="/app/.venv/bin:$PATH"
# Change ownership and switch to non-root user
RUN chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["python", "-m", "your_package.main"]
Add a health check
Monitor the health of your container and enable automatic recovery:
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8000/health || exit 1
Use .dockerignore
Exclude unnecessary files from your Docker build context:
__pycache__/
*.py[cod]
*$py.class
.pytest_cache/
.coverage
htmlcov/
.git/
.github/
.venv/
.vscode/
*.md
!README.md
Optimize for production builds with build arguments
Use build arguments to toggle between development and production builds:
ARG ENV=production
RUN if [ "$ENV" = "production" ] ; then \
poetry install --only main ; \
else \
poetry install ; \
fi
Separate dependency installation from code changes
To further optimize build caching:
# Copy and install dependencies first
COPY pyproject.toml poetry.lock* ./
RUN --mount=type=cache,target=/root/.cache/pypoetry \
poetry install --only main --no-root
# Then copy application code (which changes more frequently)
COPY . .
By following these practices, you'll create Docker images for your Python applications that are efficient, secure, and optimized for both development and production environments.
Last updated on