Repository navigation
Conversation
…on Cloud Run modifyConfig now sets images.unoptimized: true in memory during production builds, as App Hosting does. It leaves the images config unchanged when: - the app uses a custom image loader (images.loader other than "default", or images.loaderFile), because unoptimized: true would bypass the loader; - the build sets GOOGLE_NEXTJS_IMAGE_OPTIMIZATION=true (or "1"). This is the only way to opt back in: modifyConfig receives the config after Next.js fills in its defaults, so an explicit unoptimized: false can't be told apart from no setting; - images.unoptimized is already true. One build log line says which case applied. The README documents the behavior, the env var, and the custom-server caveat.
Contributor
There was a problem hiding this comment.
Code Review
This pull request disables Next.js image optimization by default on Cloud Run by setting images.unoptimized to true during the build phase. This default behavior can be bypassed if a custom image loader is configured or if the GOOGLE_NEXTJS_IMAGE_OPTIMIZATION environment variable is set to true or 1. The changes include the core logic implementation in modify-config.ts, corresponding unit tests in adapter.spec.ts, and updated documentation in README.md. No review comments were provided, and there is no additional feedback.
abhis3
marked this pull request as ready for review
November 10, 2026 00:22
abhis3
requested review from
Yuangwang,
annajowang,
falahat and
leoortizz
as code owners
November 10, 2026 00:22
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
@google-cloud/adapter-nextjsnow turns Next.js image optimization off by default, as@apphosting/adapter-nextjsdoes on App Hosting. Duringnext build,modifyConfigsetsimages.unoptimized: truein memory. The app'snext.configfile isn't touched. Pages render plain<img src="/...">tags that point at the original files, and/_next/imagereturns 404.imagesconfig/_next/imageunoptimized: trueset in memory<img src="/logo.png">next.config(images.loaderother than"default", orimages.loaderFile)GOOGLE_NEXTJS_IMAGE_OPTIMIZATION=true(or1) at build time/_next/image?url=...images.unoptimized: truealready innext.config<img src="/logo.png">modifyConfigreceives the config after Next.js fills in its defaults (unoptimized: false,loader: "default",loaderFile: ""), so it can't tell an explicitunoptimized: falsefrom no setting. (@apphosting/adapter-nextjsreads the raw user config, so it can honorunoptimized: false. AmodifyConfighook can't.)unoptimized: true,next/imageskips the loader and uses the rawsrc.[adapter-nextjs] Setting NextConfig images.unoptimized=true (Next.js image optimization is off by default on Cloud Run; set GOOGLE_NEXTJS_IMAGE_OPTIMIZATION=true to turn it on).unoptimized: truetoo. Next.js requires that fornext/imagewith the default loader underoutput: 'export'.Why
The optimizer runs inside the serving instance, which isn't sized for it by default, and optimized images aren't persisted, so every new instance optimizes them again. In a local test of a 1 CPU / 512 MiB container, 80 concurrent requests (Cloud Run's default concurrency) for a 12 MP photo ran it out of memory in about 7 s, and all 80 requests failed.
A TODO in
modify-config.tstracks turning the optimizer back on, for example with a concurrency limit on/_next/imagein serve.js and a disk-cache size based on instance memory.Caveat: custom servers
modifyConfigonly changes the build. An app that starts Next.js from its own server (for examplenext({ dev: false })inserver.js) loadsnext.configagain at runtime. So its/_next/imageendpoint stays live, even though its pages render plain<img>tags. The README documents this.Changes
src/adapter/modify-config.ts: the images logic, plus anisEnvFlagEnabled()helper thatCRASHLYTICS_UPLOAD_MAPPINGnow uses too (same parsing as before).src/adapter.spec.ts: 7 new tests:imagesfields kept, input not mutated)imagesfieldloaderloaderFiletrue,TRUEor1unoptimized: trueis kept, with or without the env varREADME.md: a new "Image Optimization" section, amodifyConfigbullet, and an env var row.Testing
npm testinpackages/@google-cloud/adapter-nextjs, on Node 18.20.8 and Node 22.23.4: 44 passing, 11 pending. The pending ones are theSMOKE_TEST=1suites, the same as on main.npm run buildis clean on both.npm run lint:quietpasses, with no new warnings in the touched files.next@16.4.3builds of a sample app (App Router +next/image), withNEXT_ADAPTER_PATHpointing at this branch and served bynode .next/standalone/server.js:required-server-files.jsonhasunoptimized: true. The HTML has<img ... src="/logo.png"/>and no/_next/image./_next/image?url=%2Flogo.png&w=32&q=75returns 404, and/logo.pngreturns 200image/png.GOOGLE_NEXTJS_IMAGE_OPTIMIZATION=true:unoptimizedstaysfalse. The HTML uses a/_next/image?url=%2Flogo.png&w=…srcset, and/_next/imagereturns 200image/webp.images.loaderFile: the config is untouched, and the HTML uses the custom loader's URLs. When onlyloaderFileis set, Next.js keepsloader: "default", which is why the adapter checksloaderFiletoo..cloudrun/serve.jsinstead, the full route checks (cache guard plus the image routes) pass for both a default build and an opt-in build.