Amplify Monorepo Deployment — Lessons Learned (March 11, 2026)

The Problem: Sliplynk 29 Failed Builds

pnpm monorepo on Amplify failed due to:

  1. @types/react version mismatch between workspaces (18.x in mobile, 19.x in frontend)
  2. pnpm symlinks not followed by Amplify’s deployment step (“missing next dependency”)
  3. YAML reserved characters in amplify.yml causing parse errors
  4. $CODEBUILD_SRC_DIR + --filter doesn’t work with appRoot: setting

MANDATORY Config for pnpm Monorepo on Amplify

1. Root .npmrc (CRITICAL — this is what fixed it)

node-linker=hoisted
auto-install-peers=true
strict-peer-dependencies=false
shamefully-hoist=true

Without node-linker=hoisted, pnpm creates symlinks that Amplify can’t follow at deployment time.

2. Frontend .npmrc

legacy-peer-deps=true
prefer-workspace-packages=true
shamefully-hoist=true

3. amplify.yml (keep it simple)

version: 1
applications:
  - appRoot: frontend
    frontend:
      phases:
        preBuild:
          commands:
            - npm install -g pnpm@8.10.0
            - cd .. && pnpm install --frozen-lockfile && cd frontend
        build:
          commands:
            - pnpm run build
      artifacts:
        baseDirectory: .next
        files:
          - "**/*"
      cache:
        paths:
          - node_modules/**/*
          - .next/cache/**/*
          - ~/.pnpm-store/**/*
  • Do NOT use runtime: block with applications: key
  • Do NOT use $CODEBUILD_SRC_DIR with --filter when appRoot is set
  • Quote commands with : or || characters

4. next.config.mjs — webpack resolution for hoisted node_modules

import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
 
const nextConfig = {
  webpack: (config) => {
    const parentNodeModules = path.resolve(__dirname, '..', 'node_modules');
    if (!config.resolve.modules) config.resolve.modules = [];
    config.resolve.modules.unshift(parentNodeModules);
    config.resolve.alias = { ...config.resolve.alias, '@': path.resolve(__dirname) };
    return config;
  },
}

5. pnpm.overrides in root package.json

Force single @types/react version across all workspaces to prevent type mismatch on Linux:

"pnpm": {
  "overrides": {
    "@types/react": "19.0.7",
    "@types/react-dom": "19.0.3"
  }
}

6. Pin @types/react EXACTLY (no ~ or ^)

"@types/react": "19.0.7"     // CORRECT — exact
"@types/react": "~19.0.7"    // WRONG — allows 19.0.14 which has bugs
"@types/react": "^19.0.7"    // WRONG — allows any 19.x

What Does NOT Work

  • output: 'standalone' in next.config — does NOT fix the “missing next” error with pnpm
  • runtime: versions: node: 20 — invalid YAML when used with applications: key
  • --filter frontend with $CODEBUILD_SRC_DIR — finds no projects when appRoot is set
  • Tilde ~ or caret ^ for @types/react — buggy patch versions get installed

Reference Projects

  • Empress Eats (/Volumes/X10-Pro/Native-Projects/clients/empresss-eats/) — working pnpm monorepo on Amplify, uses hoisted node_modules
  • WCR (/Volumes/X10-Pro/Native-Projects/clients/world-cup-ready/) — working but uses npm (not pnpm)