Docker multi-stage builds image size

I have been fixing Dockerfile in our Labzero project as a preparation for PyconMY 2023 next week. I’ve been using multi-stage builds for quite some time to reduce the final image size. The general pattern is something like this:-

FROM python:3.10-buster as py-build

# [Optional] Uncomment this section to install additional OS packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
     && apt-get -y install --no-install-recommends netcat util-linux \
        vim bash-completion yamllint postgresql-client python3-dev \
	libpq-dev

COPY . /app
WORKDIR /app
ENV PATH=/opt/poetry/bin:$PATH
RUN ls /opt/poetry
RUN poetry config virtualenvs.in-project true && poetry install

FROM node:14.20.0 as js-build

COPY . /app
WORKDIR /app
RUN npm install && npm run production

FROM python:3.10-slim-buster

EXPOSE 8000
COPY --from=py-build /app /app
COPY --from=js-build /app/static /app/static
WORKDIR /app
CMD /app/.render/run.sh

The build is split into two parts. In the first part (py-build) we’re building all the Python dependencies as this is a Python app. In the second part (js-build) we’re building JS dependencies for the frontend part. The idea is that after the build is done, we can just copy the results, in case of Python the app dir + .venv directory while for the frontend, the generated css and js assets.

This actually save quite a lot. If we’re not using multi-stage builds, the final image could be more than 1GB in size. With multi-stage builds the final image is about 300+ MB.

But it turns out copying just the venv directory is not enough for Python app. In my case about, psycopg2 failed to load because of missing libpg.so.5. Initially I try to copy only that file from /usr/lib using the same COPY --from command:-

COPY --from=py-build /usr/lib/x86_64-linux-gnu/libpq.so /usr/lib/x86_64-linux-gnu/libpq.so

But then some other files are missing as well, to the point that it just too much files to copy one by one like above. So in the end I just apt-get in the final stage to install the whole packages.

RUN apt-get update && apt-get -y install libpq-dev

That unfortunately increase the image size to almost 400MB, sigh.

Written on August 20, 2023