ADR-0044 — Vercel installs with its own Bun, from the frozen lockfile

The Vercel build installs with bun install --frozen-lockfile, using the Bun Vercel picks from bun.lock, instead of fetching Bun through bunx.

  • Status: Approved
  • Date: 2026-11-05
  • Deciders: @puncoz
  • Approved by: @puncoz
  • Approved on: 2026-11-05
  • Supersedes: ADR-0042 in part: decision 5's installCommand
  • Superseded by: —
  • Design documents: ../architecture/system.mdx. Nothing a reader sees changes, so there is no app flow (ADR-0008).

Context

  • ADR-0042 decision 5 pinned Bun by installing as bunx bun@1.4.4 install --frozen-lockfile.
  • The first build of main to start after ADR-0043 (commit f94821b) failed in the install, which printed "Resolving dependencies", "Resolved, downloaded and extracted [29]" and "Saved lockfile", then Command "bunx bun@1.4.4 install --frozen-lockfile" exited with 1. Bun 1.4.4 never reported on the lockfile. When it refuses a lockfile, it prints that the lockfile is frozen.
  • main's package.json and bun.lock list the same ranges, every dependency in the lockfile has an entry, and the owner's bun.lock is the one on main.
  • The npm package bun@1.4.4 sets up its binary in a postinstall script (node install.js). The likely cause is that this did not happen when bunx installed it on Vercel. This was not reproduced, because that means running an install.
  • The owner's last successful build (e2f4ca7, before vercel.json existed) used Vercel's own install. Its log reads "Detected bun.lock generated by bun@1.4.x", then bun install v1.4.4 (38afee6c8). That is the same Bun build as the owner's machine. Vercel documents only "Bun 1" and picks the version from the lockfile.

Decision

  1. "installCommand": "bun install --frozen-lockfile". The install uses the Bun Vercel picks from bun.lock, which is 1.4.4 today, and fails when the lockfile does not match package.json. Bun is not fetched through bunx or npx.

The rest of ADR-0042 decision 5 (Bun 1.4.4 as the owner's version, the lockfile binding), as amended by ADR-0043 (Node 26), stands.

Options considered

OptionProsConsVerdict
bun install --frozen-lockfile, Vercel's BunRan on Vercel as Bun 1.4.4 (38afee6c8); nothing downloaded; the lockfile is bindingThe exact patch version is Vercel's pick; a new Bun is seen only in the build logChosen
npx --yes bun@1.4.4 install --frozen-lockfileAn exact pin; npm runs Bun's setup scriptNever run on Vercel here; downloads Bun every buildRejected
No installCommand (Vercel's default)Exactly the last successful buildThe lockfile is not enforced: a mismatch is re-resolved silentlyRejected
Keep bunx bun@1.4.4The ADR-0042 pinFails on VercelRejected

Consequences

Positive

  • The install that has worked on Vercel is used again, and the lockfile stays binding.
  • No Bun download per build.

Negative / accepted trade-offs

  • If Vercel ever picks a different Bun for this lockfile, the build log shows it first. The owner compares it with their own (bun --version).

Content invariants

None touched: the same pages are built from the same lockfile.

Security considerations

  • Supply chain. --frozen-lockfile still installs exactly bun.lock. Bun itself now comes from Vercel's build image rather than from npm, so the npm bun package is no longer downloaded or run in the build.
  • Residual risk: the Bun version is Vercel's choice, read from the lockfile, and visible in each build log.

Implementation notes

  • vercel.json: "installCommand": "bun install --frozen-lockfile".
  • src/lib/vercel-config.test.ts: the install command is exactly that, and neither the install nor the build command uses bunx.
  • Not testable here: the next build of main installs with bun install v1.4.4 and passes the frozen check.
  • bun run test, bun run types:check and bunx next build pass. bun run lint still fails with "typescript-eslint does not support TS 7.0".

Docs to update on implementation

  • content/docs/maintain/architecture/system.mdx: the installCommand row.
  • AGENTS.md §5: the Hosting row.
  • content/docs/maintain/adr/index.mdx: the row and the next free number. ADR-0042's Superseded by.

On this page

4 min read