Skip to content

Conversation

@shendongming
Copy link
Contributor

@shendongming shendongming commented Nov 27, 2025

Problem

Distroless Docker image build has been failing in CI/CD since v1.1.45 (4+ months ago).
This prevented the distroless image from being published to Docker Hub.

Error: exec /bin/sh: no such file or directory

Root cause: Distroless base image does not contain shell, but Dockerfile used heredoc
syntax which requires /bin/sh to execute.

Solution

  1. Create all symlinks in build stage (where shell is available)

    • ln -s /usr/local/bin/bun /usr/local/bin/bunx
    • ln -s /usr/local/bin/bun /usr/local/bun-node-fallback-bin/node
  2. In distroless stage, only COPY symlinks (no RUN commands needed)

    • COPY --from=build /usr/local/bin/bunx
    • COPY --from=build /usr/local/bun-node-fallback-bin/
  3. Upgrade base image from debian11 to debian12

    • Fixes security vulnerabilities (1 HIGH CVE → 0)
    • Uses gcr.io/distroless/base-debian12

Testing

  • ✅ Local build: Success (linux/amd64)
  • ✅ Azure-dev build: Success (linux/amd64)
  • ✅ Verified symlinks: All working (bun, bunx, node)
  • ✅ Image size: 228MB (comparable to debian variant at 221MB)

Related Issues

Closes #20414
Closes #16666

Related PRs

Related to #22601, #19788


Testing performed:

# Local test
docker buildx build --platform linux/amd64 --load -t bun-distroless-test .

# Image size comparison
docker images | grep bun
# bun-distroless-test  azure-dev   6042b7f2f01d   228MB
# oven/bun            1-debian    63ccc27e1e1d   221MB

# Verify bun
docker run --rm bun-distroless-test --version
# Output: 1.3.3 ✅

# Verify bunx symlink
docker run --rm --entrypoint /usr/local/bin/bunx bun-distroless-test --version
# Output: 1.3.3 ✅

# Verify node symlink
docker run --rm --entrypoint /usr/local/bun-node-fallback-bin/node bun-distroless-test --version
# Output: (works as expected) ✅

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

Walkthrough

Base image changed to Debian 12 distroless; build stage now creates bunx symlink and a /usr/local/bun-node-fallback-bin directory with a node shim; runtime stage copies those artifacts from build instead of using a mount-based RUN, and PATH/ENV variables for bun and runtime cache were adjusted.

Changes

Cohort / File(s) Summary
Distroless Dockerfile Update
dockerhub/distroless/Dockerfile
Switched base from gcr.io/distroless/base-nossl-debian11 to gcr.io/distroless/base-debian12; added build-stage creation of /usr/local/bin/bunx symlink and /usr/local/bun-node-fallback-bin/node shim; removed mount-based RUN heredoc; COPYs now transfer bunx, bun, and the fallback-bin dir to runtime; updated PATH and added BUN_RUNTIME_TRANSPILER_CACHE_PATH ENV.

Pre-merge checks

✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the two primary changes: fixing distroless build failure and upgrading to debian12, directly matching the main objectives.
Description check ✅ Passed The PR description thoroughly documents the problem, solution, testing, and verification. It exceeds template requirements with detailed troubleshooting context and test commands.
Linked Issues check ✅ Passed Changes directly address both linked issues: fixes build failure preventing distroless image publishing (#20414) and enables distroless support for current bun versions (#16666).
Out of Scope Changes check ✅ Passed All changes focus on fixing the distroless build failure and upgrading the base image; no unrelated modifications or scope creep detected.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1cd278f and e669ca4.

📒 Files selected for processing (1)
  • dockerhub/distroless/Dockerfile (2 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Write JS builtins for Bun's Node.js compatibility and APIs, and run `bun bd` after changes
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Use `bun bd` or `bun run build:debug` to build debug versions for C++ and Zig source files; creates debug build at `./build/debug/bun-debug`
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : When spawning Bun processes in tests, use `bunExe` and `bunEnv` from `harness` to ensure the same build of Bun is used and debug logging is silenced
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/js/node/test/{parallel,sequential}/*.js : For test/js/node/test/{parallel,sequential}/*.js files without a .test extension, use `bun bd <file>` instead of `bun bd test <file>` since these expect exit code 0 and don't use bun's test runner
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Add tests for new Bun runtime functionality
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24880
File: packages/bun-vscode/package.json:382-385
Timestamp: 2025-11-20T19:51:32.288Z
Learning: In the Bun repository, dependencies may be explicitly added to package.json files (even when not directly imported in code) to force version upgrades on transitive dependencies, particularly as part of Aikido security scanner remediation to ensure vulnerable transitive dependencies resolve to patched versions.
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Execute files using `bun bd <file> <...args>`; never use `bun <file>` directly as it will not include your changes
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/cli/**/*.{js,ts,jsx,tsx} : When testing Bun as a CLI, use the `spawn` API from `bun` with the `bunExe()` and `bunEnv` from `harness` to execute Bun commands and validate exit codes, stdout, and stderr
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Applies to src/js/{builtins,node,bun,thirdparty,internal}/**/*.{ts,js} : Use `process.platform` and `process.arch` for platform detection; these values are inlined and dead-code eliminated at build time
📚 Learning: 2025-11-24T18:37:11.466Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Write JS builtins for Bun's Node.js compatibility and APIs, and run `bun bd` after changes

Applied to files:

  • dockerhub/distroless/Dockerfile
📚 Learning: 2025-11-24T18:34:55.173Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Use `bun bd` or `bun run build:debug` to build debug versions for C++ and Zig source files; creates debug build at `./build/debug/bun-debug`

Applied to files:

  • dockerhub/distroless/Dockerfile
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : When spawning Bun processes in tests, use `bunExe` and `bunEnv` from `harness` to ensure the same build of Bun is used and debug logging is silenced

Applied to files:

  • dockerhub/distroless/Dockerfile
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/cli/**/*.{js,ts,jsx,tsx} : When testing Bun as a CLI, use the `spawn` API from `bun` with the `bunExe()` and `bunEnv` from `harness` to execute Bun commands and validate exit codes, stdout, and stderr

Applied to files:

  • dockerhub/distroless/Dockerfile
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/js/node/test/{parallel,sequential}/*.js : For test/js/node/test/{parallel,sequential}/*.js files without a .test extension, use `bun bd <file>` instead of `bun bd test <file>` since these expect exit code 0 and don't use bun's test runner

Applied to files:

  • dockerhub/distroless/Dockerfile
📚 Learning: 2025-11-24T18:34:55.173Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Execute files using `bun bd <file> <...args>`; never use `bun <file>` directly as it will not include your changes

Applied to files:

  • dockerhub/distroless/Dockerfile
📚 Learning: 2025-11-20T19:51:32.288Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24880
File: packages/bun-vscode/package.json:382-385
Timestamp: 2025-11-20T19:51:32.288Z
Learning: In the Bun repository, dependencies may be explicitly added to package.json files (even when not directly imported in code) to force version upgrades on transitive dependencies, particularly as part of Aikido security scanner remediation to ensure vulnerable transitive dependencies resolve to patched versions.

Applied to files:

  • dockerhub/distroless/Dockerfile
📚 Learning: 2025-10-26T01:32:04.844Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 24082
File: test/cli/test/coverage.test.ts:60-112
Timestamp: 2025-10-26T01:32:04.844Z
Learning: In the Bun repository test files (test/cli/test/*.test.ts), when spawning Bun CLI commands with Bun.spawnSync for testing, prefer using stdio: ["inherit", "inherit", "inherit"] to inherit stdio streams rather than piping them.

Applied to files:

  • dockerhub/distroless/Dockerfile
🪛 Checkov (3.2.334)
dockerhub/distroless/Dockerfile

[low] 63-63: Ensure the base image uses a non latest version tag

(CKV_DOCKER_7)

🪛 Hadolint (2.14.0)
dockerhub/distroless/Dockerfile

[warning] 63-63: Always tag the version of an image explicitly

(DL3006)

🔇 Additional comments (3)
dockerhub/distroless/Dockerfile (3)

58-61: ✓ Build-stage symlink creation correctly solves the distroless shell limitation.

Creating symlinks in the build stage (where a shell is available) and copying them into the distroless runtime stage is the right approach. This avoids the exec /bin/sh: no such file or directory error that would occur if these commands were attempted during the distroless stage.


75-77: ✓ COPY and PATH logic correctly implements the fix and ensures accessibility of symlinks.

The multi-stage pattern here is sound:

  • Line 75 copies the bunx symlink to the same location as the original bun binary
  • Line 76 copies the entire bun-node-fallback-bin directory with the node shim
  • Line 77 extends PATH to make the node shim accessible, enabling Node.js compatibility

This design allows node to be invoked without a full path, while maintaining the distroless constraint (no shell needed at runtime).


63-63: The rewritten review comment has been provided above. The verification through web search confirms that:

  1. The recommendation is accurate: Distroless tags like base-debian12 are mutable and move with upstream updates, confirming the reproducibility concern is valid.

  2. Best-practice is confirmed: Multiple authoritative sources (GoogleContainerTools/distroless, Cosign/Sigstore documentation, Kyverno) recommend SHA256 digest pinning for production use.

  3. Supply-chain security is relevant: Distroless images are cosign-signed with keyless verification support, making digest pinning part of a broader security strategy.

  4. The tradeoff is fairly presented: The original comment correctly acknowledges the tradeoff between automatic security updates and reproducibility.

The rewritten comment enhances the original by emphasizing the supply-chain security aspect and clarifying that distroless tags are definitively mutable (not just "sufficiently specific").


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

## Problem
Distroless Docker image build has been failing in CI/CD since v1.1.45 (4+ months ago).
This prevented the distroless image from being published to Docker Hub.

Error: `exec /bin/sh: no such file or directory`
Root cause: Distroless base image does not contain shell, but Dockerfile used heredoc
syntax which requires `/bin/sh` to execute.

## Solution
1. Create all symlinks in build stage (where shell is available)
   - ln -s /usr/local/bin/bun /usr/local/bin/bunx
   - ln -s /usr/local/bin/bun /usr/local/bun-node-fallback-bin/node

2. In distroless stage, only COPY symlinks (no RUN commands needed)
   - COPY --from=build /usr/local/bin/bunx
   - COPY --from=build /usr/local/bun-node-fallback-bin/

3. Upgrade base image from debian11 to debian12
   - Fixes security vulnerabilities (1 HIGH CVE → 0)
   - Uses gcr.io/distroless/base-debian12

## Testing
- Local build: ✅ Success (linux/amd64)
- Azure-dev build: ✅ Success (linux/amd64)
- Verified symlinks: ✅ All working (bun, bunx, node)
- Image size: 228MB (comparable to debian variant at 221MB)

## Related Issues
Closes oven-sh#20414
Closes oven-sh#16666
Related to oven-sh#22601, oven-sh#19788
@shendongming shendongming force-pushed the claude/fix-distroless-build branch from 1cd278f to e669ca4 Compare November 27, 2025 05:29
@shendongming
Copy link
Contributor Author

Hi maintainers! 👋

I'm a developer from SAP, and we're evaluating Bun for potential use in our production environments. Our security compliance requirements are very strict, and we specifically need the distroless variant for production deployments due to its minimal attack surface and reduced CVE exposure.

This PR fixes the distroless image build that has been broken since v1.1.45 (4+ months ago). The fix has been:

  • ✅ Tested locally (linux/amd64)
  • ✅ Tested on our internal test server (azure-dev)
  • ✅ All symlinks verified (bun, bunx, node)
  • ✅ Minimal code changes (9 insertions, 14 deletions)

Could you please approve the workflow runs so we can verify this fix passes CI/CD? This would help us move forward with our evaluation and potentially adopt Bun in SAP's production infrastructure.

Thank you for your time and consideration! 🙏

Related issues: #20414, #16666
Related PRs: #22601, #19788

@shendongming
Copy link
Contributor Author

@Jarred-Sumner @dylan-conway @markovejnovic

Could you please help review and approve the workflow runs for this PR?

This fixes the distroless Docker image that has been broken for 4+ months, which is critical for our security compliance requirements at SAP.

Thank you! 🙏

@nektro
Copy link
Contributor

nektro commented Nov 29, 2025

fixed by #24055

@nektro nektro closed this Nov 29, 2025
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.

Distroless docker image is very outdated Distroless image for bun 1.2

2 participants