Skip to content

perf(docker): stop shipping build-only content in the runtime image - #1404

Open
splitsec2 wants to merge 2 commits into
calibrain:mainfrom
splitsec2:perf/docker-drop-build-toolchain
Open

splitsec2 wants to merge 2 commits into
calibrain:mainfrom
splitsec2:perf/docker-drop-build-toolchain

Conversation

@splitsec2

Copy link
Copy Markdown
Contributor

While checking why each build took so much disk on my server, I looked at what's actually in the runtime image. A good share of it is there for the build and never used after.

The C toolchain. The base stage installs gcc, g++, libffi-dev and python3-dev for building C extensions, and they stay in both the full and lite images, which is 290 MB installed. Nothing gets compiled any more. Every compiled dependency in uv.lock (cffi, gevent, greenlet, zope-interface) ships a cp314 manylinux wheel for both amd64 and arm64, and the packages that only have an sdist are pure Python. python3-dev was also pulling Debian's libpython3.13 into a 3.14 image.

The build context. COPY . . puts the whole context into /app, so tests/, docs/ and the frontend source ship too. .dockerignore now leaves out the trees nothing reads at runtime. src/ can't go in .dockerignore because the frontend-builder stage needs it, so both final stages remove it after the built dist is copied. The venv's own pip goes as well, since uv seeds one and nothing installs at runtime. This part is @DrNgo's work from his fork, and the commit carries his name.

Measured by building both targets on native amd64 and arm64 runners:

amd64 full amd64 lite arm64 full arm64 lite
before 1,532 MB 589 MB 1,528 MB 619 MB
after 1,238 MB 295 MB 1,251 MB 342 MB

Compressed, the full image goes from 617 MB to 504 MB on amd64.

On both architectures the image builds, reaches healthy, and Chromium starts the same way the bypasser starts it (xvfb, _get_browser_args()). The Python suite passes too. It's also running on my own install now.

If a dependency ever needs compiling again, the clean fix is a builder stage that builds the wheel and copies it in, rather than putting the toolchain back in the runtime image. Happy to add that now if you'd rather have it in place.

splitsec2 and others added 2 commits September 28, 2026 15:29
The base stage installed gcc, g++, libffi-dev and python3-dev to build
C extensions, and they stayed in both the full and lite images: 290 MB
installed. Nothing is compiled any more. cffi, gevent, greenlet and
zope-interface all ship cp314 manylinux wheels for amd64 and arm64, and
the sdist-only packages in uv.lock are pure Python. python3-dev also
pulled Debian's libpython3.13 into a Python 3.14 image.
`COPY . .` put the whole build context into /app, including tests/, docs/
and developer-tool caches. .dockerignore now excludes them. It applies to
every stage, so src/ can't be listed there (frontend-builder needs it); the
final stages remove it after the built dist is copied. data/ stays, since
languages.py reads data/book-languages.json at runtime, and genDebug.sh
stays because the final stage chmod +x's it.

Also drops the venv's pip, which uv seeded alongside the system pip.
Nothing installs at runtime. setuptools stays: deps still import
pkg_resources.

Extracted from DrNgo/shelfmark-fork@075caff and re-applied onto upstream
v1.4.0 by Rob Martin, without that commit's ffmpeg and Mesa/LLVM removals.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants