Skip to content

Dockerfile: recursive chown /actions-runner after tarball extract#585

Merged
myoung34 merged 1 commit intomyoung34:masterfrom
bitranox:dockerfile-recursive-chown-runner
Apr 16, 2026
Merged

Dockerfile: recursive chown /actions-runner after tarball extract#585
myoung34 merged 1 commit intomyoung34:masterfrom
bitranox:dockerfile-recursive-chown-runner

Conversation

@bitranox
Copy link
Copy Markdown
Contributor

@bitranox bitranox commented Apr 15, 2026

What

Change the build-time ownership fix-up from

chown runner /_work /actions-runner /opt/hostedtoolcache

to

chown -R runner /_work /actions-runner /opt/hostedtoolcache

i.e. make it recursive. /_work and /opt/hostedtoolcache are empty at this point so -R is a no-op for them; the meaningful change is that every file extracted from the actions-runner release tarball is now explicitly runner-owned in the resulting image layer.

Why - the invariant that #583 relies on

#583 stopped walking /actions-runner/bin (~50 MB) and /actions-runner/externals (~330 MB, ~9 000 files) on every container start, on the grounds that they are already runner:runner in the image. That observation is correct today, but why is it correct? Tracing it:

  • The old Dockerfile chown runner /_work /actions-runner /opt/hostedtoolcache was non-recursive - it only flipped the three top-level directory inodes.
  • install_actions.sh extracts the tarball with tar -zxf, which (running as root) preserves the UIDs/GIDs stored inside the archive.
  • Those UIDs happen to match this image's runner user (UID 1001 in the base image). That's a coincidence of how GitHub publishes the release tarball, not something this repo establishes.

So entrypoint.sh's optimisation is load-bearing on upstream tarball packaging conventions. If GitHub ever re-rolls the tarball with different UIDs, or if a downstream fork rebuilds the base image with a different runner UID, ownership under bin/ and externals/ would silently drift and the entrypoint would no longer fix it.

Making the chown recursive here establishes the invariant in this repo:

  • The entrypoint's skip-list in entrypoint: skip recursive chown over /actions-runner/{bin,externals} #583 now has a locally-enforced precondition.
  • Derived images (FROM myoung34/github-runner:...) inherit correctly-owned files regardless of tarball UID layout.
  • Forks that rebuild the base with a different runner UID get correct ownership automatically, without having to remember to add their own chown -R.

Cost

Paid once at image build time, inside the same RUN that extracts the tarball - so no new layer, and the chown metadata lands in a layer that is already being written. No measurable impact on image size; ~9 000 extra chown syscalls during build, which is noise compared to the tarball download and dependency install in the same step.

No runtime cost - this is the opposite of the runtime cost that #583 removed.

Scope

  • Changed: one character (chownchown -R) on the Dockerfile's existing fix-up line.
  • Unchanged: everything else - install_actions.sh, entrypoint.sh, the base image, _work / opt/hostedtoolcache handling.

Relation to prior work

Direct follow-up to #583 ("entrypoint: skip recursive chown over /actions-runner/{bin,externals}") - I flagged this hardening as an open question in that PR's description. Same spirit as #268 (narrowing the /opt/hostedtoolcache chown) in that both favour doing ownership work at the right point in the image lifecycle rather than repeating it on every container start.

Harden the invariant that entrypoint.sh #583 now relies on: every file
under /actions-runner is runner-owned in the image.

Previously this was true only because GitHub's actions-runner release
tarball happens to encode UIDs that coincide with this image's runner
user (UID 1001). The non-recursive chown in this RUN step only touched
the three top-level directories; ownership of the ~9 000 files extracted
from the tarball was inherited verbatim from the archive metadata.

Making the chown recursive establishes the invariant in this repo
instead of inheriting it by coincidence:

- entrypoint.sh #583 skips chown -R over bin/ and externals/ on every
  container start; that optimisation now has a locally-enforced
  precondition rather than one that depends on upstream tarball
  packaging conventions.
- Derived images (FROM myoung34/github-runner:...) no longer depend on
  the tarball's happen-to-be-runner-owned ownership either.
- Forks that rebuild the base image with a different runner UID get
  correct ownership without having to remember to re-chown.

Cost is paid once at image build time, inside the same RUN that
extracts the tarball, so it does not add a new layer or inflate image
size beyond the chown metadata in the existing layer.
@myoung34
Copy link
Copy Markdown
Owner

Less ai in the future please

@bitranox
Copy link
Copy Markdown
Contributor Author

I referred to that follow up in my previouse contribution, the code is minimal, defensive and reasonable.
Lets concentrate on "what" not "how" - the PR is literally two chars , not 10 000 lines of ki slop and solves a real problem.
The explanation is a bit lengthy but that change kind of needs a good explanation why .... because there is no immideate need to merge that - its only defensive as explained.

thank You so much to merge my previouse PR - atm I swap out that line via SED before starting my containers ... that bug stalled i/o on one of my proxmox servers big time...

greetings from Vienna (if You ever come here, lets compare our beers)
Robert

@myoung34
Copy link
Copy Markdown
Owner

Sorry I meant just in the description etc, it's very obtuse and conflated but the pr is fine

@myoung34 myoung34 merged commit 51ce6df into myoung34:master Apr 16, 2026
11 checks passed
@bitranox bitranox deleted the dockerfile-recursive-chown-runner branch April 16, 2026 13:09
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