How to use
- Copy the installation command and review the source package before installing it in your own agent environment.
- Check the source document for required tools, credentials and supported hosts.
- Describe your input and desired deliverable; review the output against the source material.
npx skills add https://github.com/olsonbd/superpowers-antigravity --skill deploying-payload-to-cloudflareBefore you start
- Installation does not include a model subscription, API allowance or permission to access your files.
- Repository stars and installation counts describe activity, not an independent quality or security test.
Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.
Deploying Payload CMS to Cloudflare Workers
Overview
Deploying Payload CMS to Cloudflare Workers often hits the 3 MiB bundle size limit and encounters issues with native dependencies like sharp and @vercel/og. This skill provides a proven strategy to overcome these limitations by pruning unused files and patching imports.
When to Use
- Deploying Payload CMS to Cloudflare Workers.
- Encountering "Bundle size exceeds 3 MiB" errors.
- Encountering "Could not resolve" errors for
sharp,resvg.wasm, oryoga.wasm.
The Strategy
- Build: Use
@opennextjs/cloudflareto build the worker. - Prune: Remove the massive
node_modulesdirectory from the output. The worker bundle (handler.mjs) already contains the necessary code (mostly). - Patch: Replace absolute paths to missing native modules (which were in the pruned
node_modules) with an empty module stub.
Risks & Trade-offs
By removing node_modules, you are forcing the Worker to rely 100% on the bundled code.
- Native Modules will fail: Libraries like
sharp(C++ binary) cannot be bundled. They must exist on disk. Removingnode_modulesbreaks them. - Mitigation: Disable features relying on them (e.g.,
images: { unoptimized: true }) or use external services (e.g., Cloudflare Images). - Dynamic Imports may fail: If code uses
await import('some-lib')and that lib wasn't bundled, it will fail at runtime. - Mitigation: Ensure
output: 'standalone'is used, which is good at tracing dependencies. Test critical paths.
Implementation
1. Create build-worker.sh Script
Create a script (e.g., scripts/build-worker.sh) with the following content. This script handles building, pruning, and patching to ensure the worker fits within Cloudflare's limits.
#!/bin/bash
set -e
# 1. Build
# Check if we are being called by OpenNext (which calls 'npm run build' internally)
if [ "$IS_OPENNEXT_BUILD" = "true" ]; then
echo "Running internal Next.js build..."
npm run build:next
exit 0
fi
echo "Building OpenNext worker..."
export IS_OPENNEXT_BUILD=true
npx opennextjs-cloudflare build
# 2. Prune node_modules
# The worker bundle is self-contained enough that we don't need the full node_modules
# But we need to keep some externalized packages like graphql
echo "Pruning node_modules..."
rm -rf .open-next/server-functions/default/node_modules
mkdir -p .open-next/server-functions/default/node_modules
# Copy graphql if it exists, as it's often externalized
if [ -d "node_modules/graphql" ]; then
cp -r node_modules/graphql .open-next/server-functions/default/node_modules/
fi
# 3. Create Empty Stubs
echo "Creating stubs..."
# Stub for WASM modules
echo "export default {};" > .open-next/server-functions/default/empty_wasm.js
# Stub for JS libraries (prettier, sharp, etc.)
echo "export default {};" > .open-next/server-functions/default/empty_lib.js
# 4. Patch handler.mjs
echo "Patching handler.mjs..."
# Detect handler path (CI vs Local structure)
if [ -f ".open-next/server-functions/default/backend/handler.mjs" ]; then
HANDLER_PATH=".open-next/server-functions/default/backend/handler.mjs"
else
HANDLER_PATH=".open-next/server-functions/default/handler.mjs"
fi
if [ ! -f "$HANDLER_PATH" ]; then
echo "Error: $HANDLER_PATH not found!"
exit 1
fi
# Replace absolute paths to WASM files with empty_wasm.js
# Matches both single and double quotes, and optional ?module suffix
perl -pi -e 's|(["'"'"'])[^"'"'"']*node_modules/[^"'"'"']+\.wasm(\?module)?\1|\1./empty_wasm.js\1|g' "$HANDLER_PATH"
# Stub .ttf.bin files (often used by @vercel/og)
perl -pi -e 's|(["'"'"'])[^"'"'"']*node_modules/[^"'"'"']+\.ttf\.bin\1|\1./empty_lib.js\1|g' "$HANDLER_PATH"
# Patch specific problematic libraries (prettier, sharp)
perl -pi -e 's|require\("prettier"\)|require("./empty_lib.js")|g' "$HANDLER_PATH"
perl -pi -e 's|import\("prettier"\)|import("./empty_lib.js")|g' "$HANDLER_PATH"
perl -pi -e 's|require\("sharp"\)|require("./empty_lib.js")|g' "$HANDLER_PATH"
perl -pi -e 's|import\("sharp"\)|import("./empty_lib.js")|g' "$HANDLER_PATH"
# Fix any double-slash issues if they occur
perl -pi -e 's|"/./empty_wasm.js"|"\./empty_wasm.js"|g' "$HANDLER_PATH"
perl -pi -e 's|"/./empty_lib.js"|"\./empty_lib.js"|g' "$HANDLER_PATH"
echo "Build and optimization complete."2. Configure next.config.ts
Alias problematic dependencies to an empty file during the Next.js build to prevent them from bloating the initial bundle or causing build errors.
// next.config.ts
import { withPayload } from "@payloadcms/next/withPayload";
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
images: {
unoptimized: true, // Cloudflare handles images via a separate worker or R2
},
webpack: (config) => {
// Alias sharp and @vercel/og to empty to avoid bundling them
config.resolve.alias.sharp = "./empty.js";
config.resolve.alias["@vercel/og"] = "./empty.js";
return config;
},
};
export default withPayload(nextConfig);3. CI/CD Integration
Use Cloudflare's native CI/CD integration by connecting your repository in the Cloudflare Dashboard.
- Build Command:
bash scripts/build-worker.sh(ornpm run build:worker) - Build Output Directory:
.open-next/server-functions/default
Common Mistakes
- Not Pruning: Leaving
node_moduleswill almost always exceed the 3 MiB limit. - Not Patching: The generated
handler.mjswill contain absolute paths to the machine where it was built. These paths will fail at runtime on Cloudflare. - Using `pnpm`:
pnpm's symlinked structure can cause issues with pruning and native module resolution (ERR_DLOPEN_FAILED). Use `npm` for this strategy to ensure a flatnode_modulesstructure and better compatibility with Cloudflare's build environment. - Infinite Loops: If
npm run buildcalls this script, and this script callsnpx opennextjs-cloudflare build, which callsnpm run buildagain, you will get an infinite loop. Ensure you have a re-entry guard (as shown in the script above).

