Skip to content

Dockerfile: add multi-stage build, pin uv, improve caching and runtime user handling - #19

Merged
ThomasBury merged 1 commit into
mainfrom
codex/create-efficient-multi-stage-dockerfile
Mar 3, 2026
Merged

Dockerfile: add multi-stage build, pin uv, improve caching and runtime user handling#19
ThomasBury merged 1 commit into
mainfrom
codex/create-efficient-multi-stage-dockerfile

Conversation

@ThomasBury

@ThomasBury ThomasBury commented Mar 3, 2026

Copy link
Copy Markdown
Owner

Motivation

  • Improve build reproducibility and layer caching for faster iterative builds.
  • Reduce final image size and remove unnecessary layers by splitting build/runtime concerns.
  • Ensure the runtime container runs as an unprivileged user and avoids extra chown passes.

Description

  • Add Docker build syntax directive and convert to a multi-stage build with FROM python:3.12.10-slim AS builder and a separate runtime stage.
  • Pin uv to ghcr.io/astral-sh/uv:0.5.26 and copy only the dependency manifests pyproject.toml and uv.lock to maximize layer cache effectiveness.
  • Use uv sync --frozen --no-cache --no-install-project to install dependencies first, then copy source and data and run uv sync --frozen --no-cache to install the local project.
  • Create an unprivileged app user in the runtime stage and copy runtime artifacts from the builder with --chown=app:app to avoid an extra chown -R layer, set PATH="/app/.venv/bin:$PATH", add EXPOSE 80, and run the app via the fastapi binary on PATH.

Testing

  • Built the runtime image with docker build --target runtime -t acebet:pr . and the build completed successfully.
  • Ran a quick automated runtime check with docker run --rm acebet:pr fastapi --help and the fastapi binary executed successfully, confirming the virtualenv is on PATH.

Codex Task

Summary by CodeRabbit

  • Chores
    • Improved container build configuration for faster deployment and enhanced security practices, including optimized dependency caching and unprivileged runtime execution.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the Dockerfile to enhance the build process and runtime environment. The changes aim to improve build reproducibility, reduce the final image size by separating build and runtime concerns, and bolster security by ensuring the application runs as an unprivileged user. These modifications lead to more efficient and secure container images.

Highlights

  • Multi-stage Docker build: Implemented a multi-stage Docker build process, separating builder and runtime stages to improve build reproducibility and reduce final image size.
  • uv version pinning: Pinned the uv dependency manager to a specific version (0.5.26) to ensure reproducible dependency installations.
  • Optimized dependency caching: Enhanced Docker layer caching by copying only dependency manifests (pyproject.toml, uv.lock) before installing dependencies, and then copying source code.
  • Unprivileged runtime user: Configured the runtime container to run as an unprivileged app user, with artifacts copied using --chown=app:app to avoid extra chown layers.
  • Runtime environment configuration: Updated the PATH environment variable to include the virtual environment binaries and added EXPOSE 80 for the application.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • Dockerfile
    • Converted the Dockerfile to use a multi-stage build pattern with builder and runtime stages.
    • Updated the base Python image to python:3.12.10-slim for both stages.
    • Pinned the uv tool to version 0.5.26 for consistent dependency management.
    • Modified dependency installation steps to leverage Docker's layer caching more effectively by copying pyproject.toml and uv.lock first.
    • Introduced an unprivileged app user in the runtime stage and used --chown during COPY operations to set correct permissions efficiently.
    • Set the PATH environment variable in the runtime stage to include the virtual environment's bin directory.
    • Added EXPOSE 80 to indicate the application's listening port.
    • Adjusted the CMD instruction to directly call fastapi from the PATH.
Activity
  • The runtime image was successfully built using docker build --target runtime -t acebet:pr ..
  • An automated runtime check confirmed the fastapi binary executed successfully via docker run --rm acebet:pr fastapi --help, verifying the virtual environment is on PATH.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented Mar 3, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR refactors the Dockerfile to use a multi-stage build with Python 3.12.10-slim, pinning uv to version 0.5.26, moving dependency resolution into a builder stage, and creating an unprivileged app user with optimized layer caching and explicit file ownership configuration.

Changes

Cohort / File(s) Summary
Docker Build Optimization
Dockerfile
Converts to multi-stage build with builder and runtime stages. Pins uv (0.5.26), moves dependency resolution to builder, creates unprivileged app user, sets up global PATH for venv, copies runtime artifacts (.venv, src, data) with explicit ownership, adds EXPOSE 80, and updates CMD to use PATH-based FastAPI execution.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Hop hop! A builder stage so neat,
Multi-stage builds can't be beat!
User unprivileged, secure and tight,
Docker layers cached just right! 🎁

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes all major changes: multi-stage build, pinned uv version, improved caching strategy, and unprivileged user handling—matching the PR's core objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/create-efficient-multi-stage-dockerfile

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly improves the Docker setup by introducing a multi-stage build, which enhances reproducibility, security, and build performance. Pinning versions for both the base image and uv is a great step towards reproducible builds. The use of a non-root user and --chown in the COPY instruction are excellent security and optimization practices. I've added a couple of suggestions to further reduce image layers and improve the Dockerfile's maintainability.

Please also consider updating the documentation in docs/deployment/docker-and-github-actions.md to reflect the new multi-stage build process. Specifically, the docker build command now requires --target runtime to build the final image.

Comment thread Dockerfile
FROM python:3.12-slim
# syntax=docker/dockerfile:1.4

FROM python:3.12.10-slim AS builder

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve maintainability and reduce duplication, you could introduce a base stage for common setup like the base image, ENV variables, and WORKDIR. Both builder and runtime stages can then inherit from this base stage. This means you'd only need to update these common settings in one place.

For example:

# syntax=docker/dockerfile:1.4

FROM python:3.12.10-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1
WORKDIR /app

FROM base AS builder
# ... rest of builder stage, removing ENV and WORKDIR

FROM base AS runtime
ENV PATH="/app/.venv/bin:$PATH"
# ... rest of runtime stage, removing the other ENV vars and WORKDIR

Comment thread Dockerfile
Comment on lines +44 to +46
COPY --from=builder --chown=app:app /app/.venv /app/.venv
COPY --from=builder --chown=app:app /app/src /app/src
COPY --from=builder --chown=app:app /app/data /app/data

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

You can combine these three COPY instructions into a single one to reduce the number of layers in the final image. This aligns well with the PR's goal of optimizing layers.

To do this, you would first collect all runtime artifacts into a single directory in the builder stage, for example, by adding this step after the final uv sync (line 27):

# In builder stage
RUN mkdir /artifacts && cp -r .venv src data /artifacts

Then, you can replace these three COPY lines with a single one:

COPY --from=builder --chown=app:app /artifacts/. /app/

@ThomasBury
ThomasBury merged commit ef6458f into main Mar 3, 2026
1 check was pending
@ThomasBury
ThomasBury deleted the codex/create-efficient-multi-stage-dockerfile branch March 3, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant