Skip to content

Remove headless Chrome - #16

Merged
iHiD merged 1 commit into
mainfrom
drop-chrome
Aug 13, 2026
Merged

Remove headless Chrome#16
iHiD merged 1 commit into
mainfrom
drop-chrome

Conversation

@iHiD

@iHiD iHiD commented Aug 13, 2026

Copy link
Copy Markdown
Member

Follow-up to #15. Both image kinds render with satori now, so nothing reaches the Chrome path.

What goes

puppeteer-core and @sparticuz/chromium, plus the code and config that only existed to drive them: generateImage, RENDERER, NAVIGATION_TIMEOUT_MS, SELECTOR_TIMEOUT_MS, and the imageSelector/waitForSelector fields.

node_modules: 138M → 47M (@sparticuz/chromium is 60M of it, puppeteer-core 9.5M). The ECR image is currently 275MB; I'd expect roughly 180–200MB after this, but that's an estimate — worth checking against ECR once it's built.

It also closes the loop on the outage

Chrome screenshotted a rendered page, so it had to fetch exercism.org from a datacentre IP, which is why it needed the NAT address allowlisted in Cloudflare. That's the coupling that left every image timing out for four days.

Payloads now come over the internal ALB. Once this merges, nothing in this lambda depends on that allowlist entryip.src eq 13.40.244.182 can be removed from the Cloudflare rules.

The trade-off worth being explicit about

Until now, rolling back to Chrome was unsetting one environment variable. After this it's a revert and a deploy — a couple of minutes rather than seconds.

Solutions have been on satori for about a day (~13k/day, zero errors). Profiles have been on it for minutes. If you'd rather let profiles run overnight before giving up the cheap rollback, holding this until tomorrow costs nothing.

Two things that soften it: images already in S3 keep serving regardless, so a bad render only affects URLs nobody has requested yet; and the failure mode would show up immediately in the error rate rather than silently.

Testing

45 tests pass. Both fixtures render (profile 799ms, solution 566ms). No remaining references to puppeteer, chromium, generateImage, satoriEnabled, imageSelector, waitForSelector or the public baseUrl.

Follow-ups

  • RENDERER = "satori" is still set in terraform/image_generator/lambda.tf and becomes inert. Harmless, but worth removing.
  • Memory is the real prize. Satori peaks at 302MB against 2000MB allocated; Chrome was the reason for the headroom. Worth ~$10/mo. Test before cutting — Lambda CPU scales with memory and satori/resvg are CPU-bound, so the duration increase can eat the saving. 1024MB is a sensible first step.

Both image kinds render with satori now, so nothing reaches the Chrome
path. Dropping puppeteer-core and @sparticuz/chromium takes node_modules
from 138M to 47M.

That also removes the last reason this lambda talks to the public site.
Chrome screenshotted a rendered page, which meant fetching exercism.org
from a datacentre IP and needing the NAT address allowlisted in
Cloudflare - the coupling that broke every image for four days. Payloads
now come over the internal ALB, so the allowlist entry has nothing left
depending on it.

RENDERER, NAVIGATION_TIMEOUT_MS and SELECTOR_TIMEOUT_MS go with it; they
only ever configured the browser. RENDERER is still set in terraform and
is now inert - worth removing there separately.

Rolling back is a revert and a deploy rather than an environment
variable, so this is the point where satori stops being reversible
cheaply.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iQj4EdMfNsWP6NtFTqwUU
@iHiD
iHiD requested a review from a team as a code owner August 13, 2026 21:47
@iHiD
iHiD merged commit 207ddab into main Aug 13, 2026
1 check passed
iHiD added a commit that referenced this pull request Aug 13, 2026
#16 removed the baseUrl const but left the url: fields referencing it,
so rawPathToScreenshotData threw ReferenceError. That runs before the S3
cache check, so every request failed - including ones that would have
been served straight from cache.

url was only ever read by the Chrome renderer, so it goes with it.

Nothing caught this: node --check only parses, the unit tests don't
touch index.js, and dev/render.js calls the renderers directly and
bypasses the handler entirely. A grep for "baseUrl =" matched the
declaration and not the uses.


Claude-Session: https://claude.ai/code/session_018iQj4EdMfNsWP6NtFTqwUU

Co-authored-by: Claude Opus 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