I wanted to self-host umami, a privacy-friendly alternative to Google Analytics, for my project gaste.co. My projects already live on AWS Amplify, so I forked umami and pointed Amplify at it. Umami is a Next.js app that uses Prisma and pnpm, and that combination turned out to be a bumpy ride on Amplify.
The build failed three times, each time with a different error. This post walks through all three errors and their fixes, and ends with the complete working amplify.yml. If you landed here from a search, jump to your error.
Error 1: pnpm: command not found
[WARNING]: pnpm: command not found
The Amplify build image ships with npm and yarn, but not pnpm. Umami only supports pnpm, so the default build fails immediately.
The fix is simple: install pnpm yourself in the preBuild phase.
preBuild:
commands:
- npm install -g pnpm@10
- pnpm install --frozen-lockfile
My first attempt was npx pnpm instead of a global install. It works, but there is a trap: npx pnpm downloads the latest pnpm on every build. That floating version is exactly what caused the second error.
Error 2: ERR_PNPM_IGNORED_BUILDS
ERR_PNPM_IGNORED_BUILDS
Since version 10, pnpm no longer runs the install scripts of your dependencies by default. You have to approve each package that needs to run a build script. pnpm 11 went one step further with two breaking changes:
strictDepBuildsis nowtrueby default. If any dependency has an unreviewed build script, the install fails withERR_PNPM_IGNORED_BUILDSinstead of printing a warning.- The old
onlyBuiltDependenciessetting was removed and replaced by anallowBuildsmap.
Because I was on npx pnpm, I was silently upgraded to pnpm 11 and hit both changes at once.
Umami upstream already solved this. Its pnpm-workspace.yaml contains an allowBuilds map that approves the Prisma build scripts and explicitly blocks the rest:
allowBuilds:
'@parcel/watcher': false
'@prisma/engines': true
'@swc/core': false
esbuild: false
msw: false
prisma: true
sharp: false
My fork was behind and did not have this yet. Syncing the fork with upstream and pinning pnpm@10 fixed the error. Two lessons in one: keep your fork fresh, and pin your package manager version in CI.
Error 3: The size of the build output exceeds the max allowed size
CustomerError: The size of the build output (355048302) exceeds
the max allowed size of 230686720 bytes.
This one took real detective work. Amplify has a 220 MB limit on the deployment bundle for server-side rendered apps. Umami builds with Next.js standalone output, and my bundle came in at 355 MB.
Here is the strange part: the same build on my laptop was only 166 MB. Well under the limit. So the extra weight appears somewhere in Amplify's packaging step.
My first theory was pnpm's symlinked node_modules layout being counted twice during packaging. I forced a flat layout with node-linker=hoisted and redeployed. That saved 5 MB. Wrong theory.
Measure, do not guess
AWS documents a way to download the exact bundle Amplify produced. Get the presigned artifact URL from the failed job:
aws amplify get-job --app-id <app-id> --branch-name main --job-id <build-number>
The response contains a URL to artifacts.zip. Download it, extract it, and run du on the contents. Mine looked like this:
| What | Size |
|---|---|
.next/server sourcemap files (*.js.map) |
99 MB |
@prisma/client/runtime WASM query compilers |
75 MB |
geo/GeoLite2-City.mmdb |
63 MB |
@img/* (sharp libvips binaries) |
33 MB |
next package |
29 MB |
Suddenly the problem was obvious, and none of it was my application code:
- 99 MB of sourcemaps. Umami builds with
next build --turbo, and Turbopack writes a.mapfile next to every server chunk. The server never reads them. Pure dead weight. - 75 MB of Prisma. The Prisma client ships WASM query compilers for five databases: PostgreSQL, MySQL, SQLite, SQL Server, and CockroachDB. Each one comes in fast and small builds, in both
.jsand.mjsflavors. I use exactly one database. - 33 MB of sharp. The image library includes prebuilt binaries for both glibc and musl Linux. Amplify runs glibc, so half of that is never loaded.
- 63 MB of GeoLite2. Umami downloads this database for visitor location lookups. I decided to keep it. Amplify forwards CloudFront geo headers, so umami could live without the file, but umami throws an error if the file is missing and a request arrives without those headers. Not worth the risk.
One more gotcha
My first cleanup attempt deleted files inside .next/standalone/node_modules, and the bundle size did not move at all. It turns out Amplify does not package that folder. It rebuilds node_modules for the compute bundle from your project root, using the trace manifests Next.js generates. So the pruning has to target the root node_modules.
With the sourcemaps gone and Prisma and sharp trimmed, the bundle dropped from 355 MB to around 172 MB, and the deploy finally went green.
The complete amplify.yml
version: 1
frontend:
phases:
preBuild:
commands:
- npm install -g pnpm@10
- echo "node-linker=hoisted" >> .npmrc
- pnpm install --frozen-lockfile
build:
commands:
- pnpm run build
# Turbopack server sourcemaps, never read at runtime
- find .next -name '*.map' -type f -delete
# Prisma WASM compilers for databases I do not use
- find node_modules/@prisma/client/runtime .next/standalone/node_modules/@prisma/client/runtime -name 'query_compiler*' ! -name '*postgresql*' -delete || true
# sharp musl binaries, Amplify runs glibc
- rm -rf node_modules/@img/sharp-libvips-linuxmusl-x64 node_modules/@img/sharp-linuxmusl-x64 .next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64 .next/standalone/node_modules/@img/sharp-linuxmusl-x64
artifacts:
baseDirectory: .next
files:
- '**/*'
cache:
paths:
- node_modules/**/*
I kept node-linker=hoisted even though it was not the size fix. Amplify has known issues with pnpm symlinks, and the flat layout avoids that whole category of problems.
Takeaways
- Pin your package manager version in CI. A floating
npx pnpmhanded me a major version upgrade and a broken build for free. - Measure the artifact before forming theories. My symlink theory cost one build. Five minutes with
artifacts.zipanddufound the real problem. - Turbopack writes server sourcemaps. For any size-limited deploy target, delete them after the build.
- Know what your dependencies ship. Prisma carried four databases I will never use, and sharp carried a libc I will never run.