Image: a README change re-installs every dependency, and each re-install downloads every wheel again #1140
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#1140
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What exists today
docker/Dockerfile:61copiespyproject.toml uv.lock README.mdbefore the firstuv syncat:90, so the dependency layer's cache key includes README.md.pyproject.toml:5names it as the readme, which only the project build needs, and that is the second sync at:95, afterCOPY src..forgejo/workflows/readme.yml(#1044) rewrites README.md whenever translations land.Measured with
git log --since=2026-09-30: 33 commits touched README.md, uv.lock or pyproject.toml; 11 of them touched README.md and neither of the other two. Each of those rebuilt the dependency layer for both platforms in both builders for nothing. Checked locally that the first sync does not need the README: in a scratch directory holding onlypyproject.tomlanduv.lock,uv sync --locked --no-default-groups --extra server --extra postgres --no-install-project --dry-runresolves the full set without complaint.:90and:95pass--no-cache(the comment at:71-75gives the reason: the 296 MB cache of #154). The build stage no longer ships, so the flag now only means each rebuild of the layer downloads every wheel again.What would change
README.mdfrom:61to just before the second sync at:95(withCOPY src). This needs no decision; no test reads that COPY line.--no-cacheon both syncs withRUN --mount=type=cache,target=/root/.cache/uv(uv's own Docker guide shows this form;UV_LINK_MODE=copyis already set, which a cache mount needs). The cache lives in the builder's storage, never in a layer, so the image is unchanged; the runner keeps one more cache directory per builder. Same for theuv pip installofPOSTULO_EXTRA_PACKAGESat:131.tests/test_image_build.py:116asserts--no-cacheon every sync line and would be rewritten to assert the cache mount instead (and that no cache directory reaches the runtime stage).The candidate holds on both counts; the change is that the README part is now measured and stands alone, and only the cache mount needs the user.
What it saves or protects
small-medium: 11 of the 33 commits in the last month that invalidated the dependency layer touched only README.md (the
uv synclayer is 162 s of arm64 under QEMU per #1033, plus its amd64 copy in both builders); a uv cache mount would make the lock changes that remain mostly local copiesAccessibility
none
Decided by the maintainer (2026-10-31)
README out of the dependency layer AND a BuildKit uv cache mount instead of --no-cache, one PR
Decisions
--no-cache? Recommended: yes. The reason--no-cachewas added (a cache shipped in the image) no longer applies since the multi-stage build; the comment at:71-75would be rewritten to say so.Found by the efficiency and reliability audit of 2026-10-30 (finding I4; index postulo/postulo#1108). Checked against
mainatdaee30b15(2026-10-30).