Skip to content

2594: preserve hardlinks during COPY --from - part 1 - #630

Merged
mzihlmann merged 3 commits into
mainfrom
2594-hardlinks-part-1
Apr 15, 2026
Merged

2594: preserve hardlinks during COPY --from - part 1#630
mzihlmann merged 3 commits into
mainfrom
2594-hardlinks-part-1

Conversation

@mzihlmann

Copy link
Copy Markdown
Collaborator

Description

When copying a directory via COPY --from=<image>, kaniko passed every file to CopyFile, which creates a new independent inode for each one. Hardlinks in the source stage were silently broken, causing files that shared a single inode to be duplicated in the output image. For images that rely heavily on hardlinks (e.g. git installations), this inflates image size significantly, one reporter saw 83 MB grow to 720 MB.

CopyDir now tracks inodes across the directory walk. On the first occurrence of an inode the file is copied normally. On subsequent occurrences os.Link is used instead, preserving the hardlink. At snapshot time we already handle hardlinks correctly.

This is a first part in a series of fixes, here we are concerned with copying from a remote image, in a later PR we will address copying from local stages.

Gated behind FF_KANIKO_PRESERVE_HARDLINKS=false to preserve backwards compatibility. Becomes default in v1.28.0.

@mzihlmann
mzihlmann force-pushed the 2594-hardlinks-part-1 branch 3 times, most recently from 9848e5d to 98e46ac Compare April 7, 2026 19:53
@mzihlmann
mzihlmann marked this pull request as ready for review April 7, 2026 20:35
@mzihlmann
mzihlmann requested review from 0hlov3, BobDu, babs and nejch April 7, 2026 20:35
@mzihlmann

Copy link
Copy Markdown
Collaborator Author

to be continued here #626

@mzihlmann
mzihlmann force-pushed the 2594-hardlinks-part-1 branch 4 times, most recently from 9dcb168 to 9dc0e6a Compare April 13, 2026 09:07

@0hlov3 0hlov3 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

CopyDir passed all non-symlink, non-directory files to CopyFile, which
always created a new independent file. Hardlinks are indistinguishable
from regular files via os.Lstat, so the shared inode was silently lost.
At snapshot time, checkHardlink found Nlink==1 on each copied file and
emitted regular tar entries, the TypeLink relationship was gone.

CopyDir now tracks inodes in a map[uint64]string across the walk.
On the first occurrence of an inode the file is copied normally and the
destination path is recorded. On subsequent occurrences os.Link is used
instead of CopyFile, preserving the hardlink in the destination tree.
checkHardlink then finds Nlink>1 at snapshot time and emits the correct
tar.TypeLink entries, matching Docker/BuildKit behaviour.
@mzihlmann
mzihlmann force-pushed the 2594-hardlinks-part-1 branch from 9dc0e6a to a3d0140 Compare April 15, 2026 06:17
@mzihlmann
mzihlmann force-pushed the 2594-hardlinks-part-1 branch from a3d0140 to a4d23c4 Compare April 15, 2026 06:20
@mzihlmann
mzihlmann merged commit 73566aa into main Apr 15, 2026
12 checks passed
@mzihlmann
mzihlmann deleted the 2594-hardlinks-part-1 branch April 15, 2026 06:33
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