Skip to content

Bundler cache not working due to ephemeral workdir #904

Closed

Description

@ZimbiX

Ensure the following before filing this issue

  • I verified it reproduces with the latest version with - uses: ruby/setup-ruby@v1 (see Versioning policy)

  • I tried to reproduce the issue locally by following the workflow steps (including all commands done by ruby/setup-ruby, except for Downloading Ruby & Extracting Ruby),
    and it did not reproduce locally (if it does reproduce locally, it's not a ruby/setup-ruby issue)

Are you running on a GitHub-hosted runner or a self-hosted runner?

self-hosted runner

Link to the failed workflow job (must be a public workflow job, so the necessary information is available)

N/A

Any other notes?

We use GitHub Actions runners on AWS CodeBuild with a custom Docker image. The Bundler gems caching is unfortunately not working for us - Bundler installs all gems from scratch on every run. I had a look into why, and noticed a difference in the cache key between jobs, e.g.:

# Run 1
Cache key: setup-ruby-bundler-cache-v6-ubuntu-22.04-x64-ruby-3.3.6-wd-/codebuild/output/src3491982091/src/actions-runner/_work/better_caring/better_caring-with--without--only--Gemfile.lock-3f96ad38961e7a44e242400b976a8833512d7d013bd35c5f24b09672d2fd2667
# Run 2
Cache key: setup-ruby-bundler-cache-v6-ubuntu-22.04-x64-ruby-3.3.6-wd-/codebuild/output/src1813367680/src/actions-runner/_work/better_caring/better_caring-with--without--only--Gemfile.lock-3f96ad38961e7a44e242400b976a8833512d7d013bd35c5f24b09672d2fd2667

That unfortunately results in being unable to reuse a previous cache.

The cause of the cache key difference is the differing working directory. This looks to be done by CodeBuild for run isolation, which seems fair. I vaguely recall seeing a similar thing with Buildkite.

I looked into the source code of this action to see if the cache key could be configured, and I don't see a way to do that. It seems there is no way to avoid the inclusion of the ephemeral working directory path in the cache key:

setup-ruby/bundler.js

Lines 190 to 196 in 7372622

// cache key
const paths = [cachePath]
const baseKey = await computeBaseKey(engine, rubyVersion, lockFile, cacheVersion)
const key = `${baseKey}-${await common.hashFile(lockFile)}`
// If only Gemfile.lock changes we can reuse part of the cache, and clean old gem versions below
const restoreKeys = [`${baseKey}-`]
console.log(`Cache key: ${key}`)

One option for us could be to change to using a constant workdir; but I'm not confident we won't run into run isolation problems from doing that.

Please consider allowing omission of the workdir from the cache key. I'd suggest replacing it with another way to identify the project - perhaps the Git remote URL (with special characters substituted as necessary).

Thank you for your work on this project =)

Activity

  1. eregon commented on Apr 26, 2026

    @eregon
    Member

    One option for us could be to change to using a constant workdir; but I'm not confident we won't run into run isolation problems from doing that.

    What are you concerned about isolation?
    Isn't each build run in Docker, or at least with the directory removed between runs?
    I think it's asking for caching issues to change the working directory between runs, especially since GitHub-hosted runners don't do that.

  2. eregon commented on Apr 26, 2026

    @eregon
    Member

    IMO this seems a design mistake of AWS CodeBuild to change the working directory every time.
    AFAIK it does not provide any extra isolation, unless I'm missing something?
    This design choice is also likely to affect other caches & actions as well, I don't think it's good idea to workaround in N actions for something that's fundamentally incompatible due to a choice in AWS CodeBuild.

  3. ntkme commented on Apr 26, 2026

    @ntkme
    Collaborator

    Yes, it does break other similar actions. For example: Swatinem/rust-cache#229

  4. eregon commented on Apr 28, 2026

    @eregon
    Member

    Is there no way to configure AWS CodeBuild to use a stable workspace directory?
    Maybe via setting the GITHUB_WORKSPACE env var or so?

    If not, could you file an issue to AWS CodeBuild about this? IMO it's their choice making a mess for no reason here.

  5. deleted a comment from godinesnohemi-svg on May 30, 2026
  6. ZimbiX commented on Sep 5, 2026

    @ZimbiX
    Author

    Sorry for the delay in continuing on this.

    I couldn't find any documentation on CodeBuild using an ephemeral workdir or how to configure that.

    I've recently started using Claude (under sufferance). Taking it through this problem, impressively it quickly came up with a solution. Omitting the most repetitive parts, here it is:

    Give the GitHub Actions runner a static workdir via buildspec

    CodeBuild installs the runner into the ephemeral $CODEBUILD_SRC_DIR, so GITHUB_WORKSPACE differs on every build and ruby/setup-ruby's Bundler cache key never repeats - it embeds the working directory. Redirecting the runner directory to a static /actions-runner makes GITHUB_WORKSPACE stable, cutting the setup-ruby step from ~3m to ~14s, and fixes every other path-keyed cache at the same time.

    AWS documents that with buildspec-override:true — which all our runner jobs already use — CodeBuild installs the runner into $CODEBUILD_SRC_DIR/actions-runner and starts it from there. The runner derives _work from its own install directory and resolves that path via /proc/self/exe, so redirecting the directory to a static /actions-runner before the runner starts gives us a stable workspace.

    Verified with two runs on a spike branch:

    Run 1 (cold cache) Run 2 (warm cache)
    GITHUB_WORKSPACE /actions-runner/_work/better_caring/better_caring identical
    setup-ruby cache Saving cache Found cache for key: …-wd-/actions-runner/…
    setup-ruby step 2m 44s 14.29s

    🤖 Generated with Claude Code

    Here's the resulting buildspec.yaml (the Docker stuff was there already):

    version: 0.2
    
    phases:
      pre_build:
        commands:
          # CodeBuild gives every build a fresh ephemeral source directory (/codebuild/output/src<random>/src) and installs the GitHub Actions runner into $CODEBUILD_SRC_DIR/actions-runner, so GITHUB_WORKSPACE is different on every build. That breaks any cache keyed on the working directory - notably ruby/setup-ruby's Bundler cache, which embeds the working directory in its cache key. Pointing the runner directory at a static path gives us a stable GITHUB_WORKSPACE of /actions-runner/_work/better_caring/better_caring.
          # Deliberately swallows errors: if this phase fails, CodeBuild never starts the runner and the GitHub Actions job hangs until someone cancels it. Falling back to the ephemeral path just means slow caching, which is what we had before.
          - |
            set -x
            rm -rf /actions-runner
            mkdir -p /actions-runner
            if [ -e "$CODEBUILD_SRC_DIR/actions-runner" ] && [ ! -L "$CODEBUILD_SRC_DIR/actions-runner" ]; then
              cp -a "$CODEBUILD_SRC_DIR/actions-runner/." /actions-runner/ && rm -rf "$CODEBUILD_SRC_DIR/actions-runner"
            fi
            if [ ! -e "$CODEBUILD_SRC_DIR/actions-runner" ] || [ -L "$CODEBUILD_SRC_DIR/actions-runner" ]; then
              ln -sfn /actions-runner "$CODEBUILD_SRC_DIR/actions-runner"
            fi
            ls -la "$CODEBUILD_SRC_DIR" /actions-runner
            true
          - echo "Starting Docker daemon"
          - nohup /usr/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 &
          - timeout 15 sh -c "until docker info; do echo .; sleep 1; done"

    And here's our corresponding piece of the GitHub Actions workflow YAML that tells CodeBuild to use this buildspec:

    jobs:
      rspec:
        runs-on:
          - codebuild-stg-mable-${{ github.run_id }}-${{ github.run_attempt }}
          - image:custom-linux-[ID_NUM_REDACTED].dkr.ecr.ap-southeast-2.amazonaws.com/runner:bettercaring-rspec-latest
          - buildspec-override:true

    The code added to pre_build is not pretty, but it works.

    Thanks for your feedback and considering my PR. With a known solution now, I'll consider this issue resolved. Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions