Skip to content

perf: cut image-heavy build times ~38% and make image work observable - #397

Merged
oleeskild merged 1 commit into
mainfrom
build-performance-fixes
Aug 7, 2026
Merged

perf: cut image-heavy build times ~38% and make image work observable#397
oleeskild merged 1 commit into
mainfrom
build-performance-fixes

Conversation

@oleeskild

Copy link
Copy Markdown
Owner

Three build-performance fixes, verified against a replica of a hosted builder environment (node:22-alpine, 4Gi, 2 vCPU) on a 210-page garden with 194MB of images (baseline 210s -> 131s):

  • Drop the full-resolution "auto" rendition from eleventy-img. fillPictureSourceSets only ever references the first two widths and the fallback serves the original file, so the full-size webp/jpeg re-encodes (the most expensive sharp work by far) were generated but never referenced by any markup.

  • Set UV_THREADPOOL_SIZE=16 for the production build. sharp's async operations run on the libuv threadpool, and at the default size of 4 multi-second image encodes monopolize the pool, serializing all other file I/O (passthrough copies, template reads) behind them. 16 sits on the measured plateau (16 ~= 32); 64 destabilized the build.

  • Cap eleventy-img queue concurrency at 2 and await all pending image jobs in an eleventy.after hook. Previously generation was fire-and- forget, so builds kept doing invisible sharp work after Eleventy reported completion - on hosted builders this ran into the job timeout and got killed with no attributable error.

Also lowers --max-old-space-size from 4096 to 2048: peak heap measured far below this, and on 4Gi build containers a 4096MB heap ceiling leaves no headroom for sharp's native memory, turning heap growth into a silent container OOM kill instead of a readable error.

Three build-performance fixes, verified against a replica of a hosted
builder environment (node:22-alpine, 4Gi, 2 vCPU) on a 210-page garden
with 194MB of images (baseline 210s -> 131s):

- Drop the full-resolution "auto" rendition from eleventy-img.
  fillPictureSourceSets only ever references the first two widths and the
  <img src> fallback serves the original file, so the full-size webp/jpeg
  re-encodes (the most expensive sharp work by far) were generated but
  never referenced by any markup.

- Set UV_THREADPOOL_SIZE=16 for the production build. sharp's async
  operations run on the libuv threadpool, and at the default size of 4
  multi-second image encodes monopolize the pool, serializing all other
  file I/O (passthrough copies, template reads) behind them. 16 sits on
  the measured plateau (16 ~= 32); 64 destabilized the build.

- Cap eleventy-img queue concurrency at 2 and await all pending image
  jobs in an eleventy.after hook. Previously generation was fire-and-
  forget, so builds kept doing invisible sharp work after Eleventy
  reported completion - on hosted builders this ran into the job timeout
  and got killed with no attributable error.

Also lowers --max-old-space-size from 4096 to 2048: peak heap measured
far below this, and on 4Gi build containers a 4096MB heap ceiling leaves
no headroom for sharp's native memory, turning heap growth into a silent
container OOM kill instead of a readable error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNhBMrXmzpXoswH4bCRWqT
@oleeskild
oleeskild merged commit 64f52ff into main Aug 7, 2026
1 check passed
oleeskild added a commit that referenced this pull request Aug 20, 2026
#397 lowered --max-old-space-size from 4096 to 2048 based on a 210-page
garden. Large text-heavy gardens (~1100+ notes) exceed that: the build
dies with a V8 "allocation failure" at ~2 GB heap while the 4Gi build
container still has half its memory unused.

3072 gives those gardens room while still leaving ~1 GB on a 4Gi
container for sharp's native allocations, which was the reason #397
lowered it in the first place.


Co-authored-by: Ole Eskild Steensen <6201338+oleeskild@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant