# ADR-0044 — Vercel installs with its own Bun, from the frozen lockfile (/docs/maintain/adr/ADR-0044-vercel-installs-with-its-own-bun-from-the-frozen-lockfile)

- **Status:** Approved
- **Date:** 2026-11-05
- **Deciders:** [@puncoz](https://github.com/puncoz)
- **Approved by:** [@puncoz](https://github.com/puncoz)
- **Approved on:** 2026-11-05
- **Supersedes:** [ADR-0042](./ADR-0042-only-main-deploys-straight-to-production-on-vercel.mdx) in part: decision 5's `installCommand`
- **Superseded by:** —
- **Design documents:** [`../architecture/system.mdx`](../architecture/system.mdx). Nothing a reader sees changes, so there is no app flow
  (ADR-0008).

## Context [#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 [#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 [#options-considered]

| Option | Pros | Cons | Verdict |
|---|---|---|---|
| `bun install --frozen-lockfile`, Vercel's Bun | Ran on Vercel as Bun 1.4.4 (38afee6c8); nothing downloaded; the lockfile is binding | The exact patch version is Vercel's pick; a new Bun is seen only in the build log | **Chosen** |
| `npx --yes bun@1.4.4 install --frozen-lockfile` | An exact pin; npm runs Bun's setup script | Never run on Vercel here; downloads Bun every build | Rejected |
| No `installCommand` (Vercel's default) | Exactly the last successful build | The lockfile is not enforced: a mismatch is re-resolved silently | Rejected |
| Keep `bunx bun@1.4.4` | The ADR-0042 pin | Fails on Vercel | Rejected |

## Consequences [#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 [#content-invariants]

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

## Security considerations [#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 [#implementation-notes]

- [x] `vercel.json`: `"installCommand": "bun install --frozen-lockfile"`.
- [x] `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.
- [x] `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 [#docs-to-update-on-implementation]

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