Skip to content

[jekyll] Fix bundle install permission error for non-root user - #1991

Open
Michael Schoen (Foconis) (michaelschoen-foconis) wants to merge 3 commits into
devcontainers:mainfrom
michaelschoen-foconis:fix/jekyll-bundle-permission-error
Open

Michael Schoen (Foconis) (michaelschoen-foconis) wants to merge 3 commits into
devcontainers:mainfrom
michaelschoen-foconis:fix/jekyll-bundle-permission-error

Conversation

@michaelschoen-foconis

Description

Running bundle install as the non-root vscode user inside the Jekyll dev container fails with:

Bundler::PermissionError There was an error while trying to write to
`/usr/local/bundle/cache/<gem>.gem`. It is likely that you need to
grant write permissions for that path.

Root cause

The Ruby base image correctly sets up GEM_HOME (/usr/local/bundle) as 1777 (world-writable, sticky bit) while it is still empty:

ENV GEM_HOME /usr/local/bundle
RUN mkdir -p "$GEM_HOME" && chmod 1777 "$GEM_HOME"

The Jekyll Dockerfile then runs several gem install commands as root (gem update --system, gem install sass-embedded ..., gem install bundler jekyll github-pages). These populate /usr/local/bundle with new subdirectories (cache/, gems/, bin/, specifications/, extensions/, ...), each created with root's default 755 permissions. The 1777 on the top-level directory doesn't propagate to these newly created subdirectories, so vscode ends up unable to write into any of them — exactly where bundle install needs to write.

Why this used to work

A fix for this already existed, but it was scoped to the old RVM-based Ruby layout:

RUN chown -R "vscode:rvm" "/usr/local/rvm/" \
    && chmod -R g+r+w "/usr/local/rvm/" \
    && find "/usr/local/rvm/" -type d | xargs -n 1 chmod g+s

When the Ruby base image moved off RVM to ruby-build/rbenv (#1957, devcontainers/features#1654), this block started failing the build (chown: invalid group: 'vscode:rvm'), and was removed entirely in #1964. That removal was correct for the now-nonexistent rvm group/path, but it also happened to be the only thing keeping /usr/local/bundle group-writable after the gem install steps above — nobody re-applied an equivalent fix for the new layout, since the PR's scope was fixing the broken build, not preserving unrelated side effects of the removed block.

Fix

/usr/local/rubies (the current Ruby install location) already handles this correctly: root:ruby with 2775 (setgid + group-writable), and vscode is a member of the ruby group. This PR applies the same pattern to /usr/local/bundle:

RUN chgrp -R ruby "/usr/local/bundle" \
    && chmod -R g+rwX "/usr/local/bundle" \
    && find "/usr/local/bundle" -type d | xargs -n 1 chmod g+s

Bumped manifest.json version 2.4.12.4.2.

Validation

Built the image locally from this branch (docker build --build-arg VARIANT=3.4-bookworm ...) and tested against a real-world Gemfile/Gemfile.lock (Jekyll + AsciiDoc + PDF toolchain, 59 gems incl. native extensions like nokogiri, google-protobuf, sass-embedded, ffi):

  • Without this fix: bundle install as vscode fails immediately with Bundler::PermissionError on the very first gem it tries to cache.
  • With this fix: bundle install as vscode completes ("Bundle complete! 5 Gemfile dependencies, 59 gems now installed."), and a subsequent bundle exec jekyll build succeeds (exit 0).

Type of change

  • Bug fix (non-breaking change which fixes an issue)

RUN gem install runs as root and populates subdirectories of GEM_HOME
(/usr/local/bundle) with root-owned, non-group-writable permissions
(cache/, gems/, bin/, specifications/, ...), even though the Ruby base
image sets GEM_HOME itself to 1777. This leaves the non-root "vscode"
user unable to run `bundle install` (Bundler::PermissionError writing
to /usr/local/bundle/cache/*.gem).

A fix for this existed previously, tied to the "vscode:rvm" ownership
of /usr/local/rvm/, but was removed in devcontainers#1964 once the Ruby base image
moved off RVM (no longer creating that group/path). That removal also
dropped the only thing keeping /usr/local/bundle writable by "vscode"
after the gem installs above, since it was never re-applied to the
image's newer ruby-build/rbenv layout.

/usr/local/rubies (the current Ruby install location) already solves
this correctly: root:ruby with 2775 (setgid + group-writable), and
"vscode" is a member of the "ruby" group. Apply the same pattern to
/usr/local/bundle so bundle/gem installs as "vscode" work the same
way.

Verified locally: built the image from this branch and ran
`bundle install` / `bundle exec jekyll build` as the "vscode" user
against a real Gemfile — fails without this change
(Bundler::PermissionError), succeeds with it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Add regression coverage and synchronize the README version.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Fixes non-root bundle install failures in the Jekyll image and bumps the patch version.

Changes:

  • Makes /usr/local/bundle writable by the ruby group with setgid permissions.
  • Updates the manifest version from 2.4.1 to 2.4.2.
File summaries
File Changes and review findings
src/jekyll/.devcontainer/Dockerfile Fixes gem directory permissions. Moderate (2 votes): Add regression coverage for non-root bundle install.
src/jekyll/manifest.json Bumps the image patch version. Nit (3 votes): Update the README version reference to 2.4.2.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/jekyll/.devcontainer/Dockerfile
Comment thread src/jekyll/manifest.json
- Add a smoke test that writes into every GEM_HOME subdirectory as the
  non-root "vscode" user, so a future change to the gem-install or
  permission steps can't silently reintroduce the bundle install
  permission error. Verified it fails (exit 1, Permission denied) on
  the unpatched image and passes on the patched one.
- Update README.md's pinned version example from 2.4.1 to 2.4.2 to
  match the manifest.json bump in the previous commit.
@michaelschoen-foconis

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

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