build(web): standalone Docker image + drop Turbopack build cache (#4619)
The Docker production-stage opts into Next.js `output: 'standalone'` via a BUILD_STANDALONE env flag, so it ships only the traced runtime (server.js + hoisted node_modules + static/public) and runs `node server.js` instead of pnpm over the full source tree. The flag — and `outputFileTracingRoot`, which traces from the monorepo root so workspace packages are included — is set only in the Dockerfile build stage. Every other path keeps its original output: Tauri `export`, local `build-web`, dev, and the Cloudflare/OpenNext deploy (which forces standalone itself via NEXT_PRIVATE_STANDALONE). Disable the experimental `turbopackFileSystemCacheForBuild`: a build interrupted mid-compile leaves a partial cache that the next build mishandles, fanning out workers until it exhausts host RAM. Remove the pull-request CI step that cached `.next/cache` for it, now unused. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,18 +60,6 @@ jobs:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
|
||||
# Turbopack persists its build cache here (turbopackFileSystemCacheForBuild).
|
||||
# The key is deterministic (no commit SHA) so every PR restores the same
|
||||
# entry — in practice the one `main` last saved, which is the only cache
|
||||
# sibling PRs can all see.
|
||||
- name: cache Turbopack build cache
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: apps/readest-app/.next/cache
|
||||
key: turbo-build-web-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
turbo-build-web-${{ runner.os }}-
|
||||
|
||||
- name: install Dependencies
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
|
||||
+19
-7
@@ -45,15 +45,27 @@ COPY --from=dependencies /app/apps/readest-app/public/vendor /app/apps/readest-a
|
||||
COPY --from=dependencies /app/packages/foliate-js/node_modules /app/packages/foliate-js/node_modules
|
||||
COPY . .
|
||||
WORKDIR /app/apps/readest-app
|
||||
# Opt into the self-contained `.next/standalone` tree for this image only;
|
||||
# next.config.mjs gates `output: 'standalone'` on BUILD_STANDALONE so other
|
||||
# web builds keep their default output.
|
||||
ENV BUILD_STANDALONE=true
|
||||
RUN pnpm build-web
|
||||
|
||||
# Production runtime ships only the standalone server, its traced node_modules,
|
||||
# and the static/public assets — no pnpm, no source tree, no dev dependencies,
|
||||
# no build cache. `output: 'standalone'` (next.config.mjs) emits the self-contained
|
||||
# tree under .next/standalone, so the entrypoint is a plain `node server.js`.
|
||||
FROM docker.io/library/node:24-slim@sha256:24dc26ef1e3c3690f27ebc4136c9c186c3133b25563ae4d7f0692e4d1fe5db0e AS production-stage
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
RUN corepack prepare pnpm@11.1.1 --activate
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
WORKDIR /app
|
||||
COPY --from=build /app /app
|
||||
WORKDIR /app/apps/readest-app
|
||||
ENTRYPOINT ["pnpm", "start-web", "-H", "0.0.0.0"]
|
||||
# Monorepo-rooted standalone tree: server.js + hoisted, traced node_modules.
|
||||
COPY --from=build --chown=node:node /app/apps/readest-app/.next/standalone ./
|
||||
# Static and public assets are not part of the standalone trace; copy them next
|
||||
# to the server so their default relative paths resolve.
|
||||
COPY --from=build --chown=node:node /app/apps/readest-app/.next/static ./apps/readest-app/.next/static
|
||||
COPY --from=build --chown=node:node /app/apps/readest-app/public ./apps/readest-app/public
|
||||
USER node
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["node", "apps/readest-app/server.js"]
|
||||
|
||||
@@ -14,12 +14,23 @@ if (isDev) {
|
||||
}
|
||||
|
||||
const exportOutput = appPlatform !== 'web' && !isDev;
|
||||
// Opt-in standalone output, set only by the Docker production build
|
||||
// (Dockerfile). Every other path keeps the original behavior: Tauri `export`,
|
||||
// local `build-web` (output undefined), dev, and the Cloudflare/OpenNext
|
||||
// deploy — which forces standalone itself via NEXT_PRIVATE_STANDALONE.
|
||||
const standaloneOutput = !exportOutput && process.env['BUILD_STANDALONE'] === 'true';
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// Ensure Next.js uses SSG instead of SSR
|
||||
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
|
||||
output: exportOutput ? 'export' : undefined,
|
||||
// The Docker production image opts into a self-contained `.next/standalone`
|
||||
// tree (see Dockerfile) so it can ship only the traced runtime; all other
|
||||
// web builds fall back to the default server output.
|
||||
output: exportOutput ? 'export' : standaloneOutput ? 'standalone' : undefined,
|
||||
// Monorepo: trace from the repo root so workspace packages land in the
|
||||
// standalone tree. Only relevant to — and only set for — the Docker build.
|
||||
outputFileTracingRoot: standaloneOutput ? path.join(__dirname, '../../') : undefined,
|
||||
pageExtensions: exportOutput ? ['jsx', 'tsx'] : ['js', 'jsx', 'ts', 'tsx'],
|
||||
// Note: This feature is required to use the Next.js Image component in SSG mode.
|
||||
// See https://nextjs.org/docs/messages/export-image-api for different workarounds.
|
||||
@@ -28,11 +39,11 @@ const nextConfig = {
|
||||
},
|
||||
devIndicators: false,
|
||||
experimental: {
|
||||
// Persist Turbopack's compilation cache to `.next/` so CI can restore it
|
||||
// between runs. Dev caching is on by default since Next 16.1; build
|
||||
// caching is opt-in (beta).
|
||||
// Dev caching is on by default since Next 16.1. We deliberately do NOT
|
||||
// enable Turbopack's build cache (turbopackFileSystemCacheForBuild, beta):
|
||||
// a build interrupted mid-compile leaves a partial cache that the next
|
||||
// build mishandles, fanning out workers until it exhausts RAM.
|
||||
turbopackFileSystemCacheForDev: true,
|
||||
turbopackFileSystemCacheForBuild: true,
|
||||
},
|
||||
// Configure assetPrefix or else the server won't properly resolve your assets.
|
||||
assetPrefix: '',
|
||||
|
||||
Reference in New Issue
Block a user