Application Development

Vercel Serverless Function Size Limit Exceeded: Fix or Migrate

J
James Eriksson
··11 min read
Vercel's 250MB bundle limit stopped your deployment. Learn diagnostics, five optimization techniques, and when to migrate to containers like Ship for predictable pricing.
TL;DR
  • Vercel's 250MB bundle limit is a hard architectural constraint that prevents cold starts from degrading as functions grow larger
  • Diagnose oversized functions using VERCEL_ANALYZE_BUILD_OUTPUT=1 environment variable to see which dependencies are consuming space
  • Five concrete optimization techniques (includeFiles, tree-shaking, dependency audit, asset offload, Next.js outputFileTracing) solve most cases without leaving Vercel
  • Framework overhead (swc binaries, Puppeteer, LangChain) and transitive dependencies can make optimization impossible--migrating to containers is faster than fighting the limit
  • Fluid Compute (5GB, $$$) or managed containers (Ship, flat-fee, predictable) are your exit paths when optimization fails

The 250MB uncompressed bundle size limit on Vercel serverless functions is a hard stop that hits developers with working apps. It exists to keep cold-start latency low and memory footprint manageable. You have two paths: optimize your dependencies and code until you fit, or migrate to containers where the limit disappears entirely.

Understanding Vercel's Serverless Function Size Limits

Vercel enforces three different size limits to prevent runaway deployments and slow cold starts. The 250MB uncompressed bundle limit is the one that usually hurts: it's the total size of your function's code and all its dependencies before compression, measured at build time. Node.js and Bun functions max out at 250MB. Python functions get 500MB (raised from 250MB in October 2024). If you opt into Fluid Compute and enable Active CPU, Vercel will let you deploy functions up to 5GB uncompressed. These are hard architectural limits, not configuration mistakes--they exist because Vercel's platform model requires functions to initialize quickly and fit into memory during cold starts.

The 4.5MB payload limit is separate and often misunderstood. That's not your function size; that's the maximum size of a single HTTP request or response body sent to or from your function at runtime. A 250MB function that tries to return a 5MB response will fail on the payload limit, not the bundle limit. Both are real problems, but they need different solutions.

Why these limits exist matters. Serverless functions are designed for stateless, short-lived work. Cold starts (the time it takes Vercel to spin up your function for the first time) scale with bundle size. A 250MB function takes seconds to initialize; a 1GB function takes much longer. Vercel keeps the limit low to guarantee you consistent sub-500ms cold starts. If you break that contract, everyone's performance suffers. The limit protects the platform's economics.

Diagnosing a Serverless Function Size Exceeded Error

When you hit the limit, Vercel tells you that your deployment exceeded 250MB but not which function or what's consuming the space. The build log error message is frustratingly opaque: it shows the total size, not the breakdown. To actually see what's inside your functions, set the environment variable VERCEL_ANALYZE_BUILD_OUTPUT=1 in your Vercel project settings, then redeploy. Vercel will print a detailed size analysis in the build logs showing every function name, its uncompressed size in MB, and the top three contributors to that size.

When you look at these logs, you'll see entries like: "api/generate.func 187MB" followed by a list of the packages consuming the most space. Next.js projects often show a large.next directory (the compiled output), node_modules entries for your dependencies, and sometimes package copies that appear multiple times. The breakdown shows you exactly where the bloat lives.

One critical detail: the error only appears during deployment, not during local testing. You can run vercel build on your machine and it may succeed, then deployment fails moments later. This happens because Vercel's build environment sometimes differs from your local machine--different OS, different dependency versions, different cache state. Always test with vercel build locally before pushing, and always enable VERCEL_ANALYZE_BUILD_OUTPUT until the error is gone. GitHub discussion #4354 at vercel/community shows this asymmetry tripping up dozens of developers: "Deployed fine a couple of times, then poof."

The Five Optimization Techniques (For Staying on Serverless)

If your app can fit under 250MB (or 500MB for Python), five concrete optimization techniques will get you there without leaving Vercel. These are from Vercel's own documentation, tested by thousands of developers.

First: use includeFiles and excludeFiles in vercel.json to control what gets bundled. By default, Vercel packages your entire project. You can tell it to skip directories you don't need at runtime. Example: if your Next.js API routes don't need the public folder, excludeFiles removes it from the deployment. Your vercel.json might look like {"functions": {"api/**": {"excludeFiles": "{public,tests,docs}/**"}}}. This is coarse but effective.

Second: bundle your code and enable tree-shaking. Use esbuild, Webpack, or Rollup to bundle your source code, then tell your bundler to remove dead code (tree-shaking). If you import a function but never call it, tree-shaking deletes it from the final bundle. For Node.js, this often saves 20-40% of bundle size by eliminating unused exports and unused branches.

Third: audit your dependencies ruthlessly. Run npm ls or yarn why and look for packages you're not actually using. Many projects accumulate dependencies over time--something was useful once, then the code changed, but the dependency stayed. Remove them. Check for duplicate versions of the same package (npm dedup helps here). A single outdated copy of a large package can cost you 50MB+.

Fourth: offload static assets to a CDN or external storage. If your function reads a 30MB JSON file at startup, don't bundle it. Load it from S3 or Cloudflare R2 at runtime instead. Images, videos, and data files should live outside the function archive.

Fifth: if you're on Next.js, enable outputFileTracing in your next.config.js. This tells Next.js to include only the files that your API routes and server-side code actually need, not the entire.next directory. This single setting cuts function size 30-50% on many Next.js projects. Set it and redeploy.

When Optimization Hits the Wall (Common Sticking Points)

The GitHub discussion around this error contains over 6,000 words of community Q&A and reveals a painful pattern: optimization works for a while, then fails inexplicably on the next deploy. Three reasons explain this cycle.

First: framework overhead you can't control. Next.js automatically bundles the swc transpiler and the.next build cache, often 100-150MB of code you didn't write and can't easily prune. Puppeteer bundles a full headless Chrome browser--instantly 150MB. LangChain pulls in NumPy and SciPy; those alone are 80MB+. If your app depends on these libraries, optimization can only trim the edges. You can't optimize away the core library without abandoning it.

Second: transitive dependencies beyond your visibility. Your code imports package A, which imports B, which imports C--and C is 90MB of code your own code never touches. npm ls shows your direct dependencies, but yarn why and npm ls --depth=10 reveal the full tree. Many developers never dig into transitive dependencies and are shocked to find that a single "lightweight" import drags in a giant dependency they didn't know existed.

Third: the mysterious rebuild failure. Your code hasn't changed, but the next deployment fails. This happens when Vercel's build environment updates, your lockfile auto-updates patch versions, or a cached build artifact gets duplicated during a clean rebuild. You deploy identical code on Monday and it succeeds; Tuesday it fails. The GitHub discussion shows this driving developers to desperation: "I don't know what I changed. It just stopped working."

Option A: Fluid Compute (Stay on Vercel, Pay More)

Fluid Compute is Vercel's answer for developers who need larger functions but want to stay on their platform. Set the environment variable VERCEL_SUPPORT_LARGE_FUNCTIONS=1 and enable Active CPU (always-on execution, not cold-start). This raises the bundle limit to 5GB per function. For AI models, heavy batch processing, or Puppeteer workloads that can't be optimized further, this is a path forward.

The cost tradeoff is significant. Active CPU means you're no longer paying per invocation; you're billed for compute time. A function that runs continuously or handles high traffic will cost more on Fluid Compute than on standard serverless. Vercel doesn't publish exact pricing; you need to calculate your expected invocation volume and compare against Fluid Compute's per-CPU-second billing.

Fluid Compute makes sense if: your workload genuinely needs gigabytes of bundled code and you've already optimized as far as reason allows; you trust Vercel's ecosystem and don't want to learn Docker; or you have existing code optimized for Vercel and rewriting for containers is too costly. If your actual workload is lightweight but your dependencies are bloated, Fluid Compute is hiding the real problem. Migrate instead.

Option B: Migrate to Managed Containers (The Architecture Shift)

If optimization hits a wall or you'd rather not fight it, containers solve the problem by eliminating the limit entirely. A Docker image can be gigabytes. You set memory and CPU allocations per container, and the 250MB bundle limit vanishes.

This is a different architecture, not just a different host. Serverless functions are stateless and request-scoped; they run code in response to a trigger and exit. Containers are persistent services: your app runs in a long-lived process handling many requests. Cold starts are longer (a few seconds instead of milliseconds), but your container stays warm between requests. Scaling is different: instead of Vercel spinning up parallel functions, your container runs one process that handles concurrent connections.

The mental model shift is real. You're no longer thinking about function-per-endpoint; you're thinking about a single application server (like your local Node.js or Python process). This is simpler in some ways (no function-level visibility, one unified logging system) and more complex in others (you own the process lifecycle, not Vercel).

Most developers moving to containers choose managed platforms--Ship, Railway, Render, or Northflank--rather than raw Kubernetes. These platforms let you push a Docker image or git repo, and they handle the plumbing. No DevOps knowledge required. You still own the Dockerfile and its contents, but the platform abstracts away orchestration.

Moving Your App to Ship (Practical Off-Ramp)

If you're choosing containers, Ship's managed platform simplifies the transition. Ship is a managed container hosting platform: you push a Docker image or connect a git repository, and Ship builds and deploys it. No bundle size limits. You set memory and CPU per container and deploy.

Ship's pricing model is fundamentally different from Vercel. Instead of pay-per-use, Ship charges a flat monthly fee for your application. This removes one source of surprise that frustrates developers on Vercel: unexpected overages and bandwidth charges. Developers building side projects or small businesses value predictable flat-fee hosting over savings of a few dollars. A working app that costs $29/month reliably is better than one that costs $8/month most months but $120 when you get traffic.

Ship also offers GDPR-compliant hosting if your application handles European user data and you need data residency guarantees. Vercel's infrastructure is US-based by default.

The migration path is straightforward. If your app doesn't have a Dockerfile, Ship can auto-detect your framework (Node.js, Python, Go, Ruby, etc.) and generate one. If it does, push the repo to Ship's git integration or use the CLI. Ship builds the image and deploys it. Request tracing, logs, and performance metrics are built in, so you're not operating blind like your first Vercel deployment.

Honesty: Hetzner with a self-hosted Coolify setup wins on raw compute cost. A $3/month VPS with Coolify is cheaper than Ship's base tier. But Coolify requires you to manage Docker, backups, security updates, and debugging yourself. If your time is worth anything, Ship's predictable costs remove the mental overhead of DIY infrastructure. Pick your tradeoff based on whether you enjoy infrastructure or not.

Frequently Asked Questions

What are Vercel's bundle size limits by runtime?

Node.js and Bun functions: 250MB uncompressed. Python functions: 500MB. With Fluid Compute and Active CPU enabled: 5GB for all runtimes. These are uncompressed limits measured at build time, not runtime.

How do I identify which Vercel function is oversized?

Set VERCEL_ANALYZE_BUILD_OUTPUT=1 in your Vercel project environment variables and redeploy. The build log will print the size of every function and its top contributors. Read the build logs carefully--the breakdown shows which files and dependencies are consuming space.

What are the main reasons my Vercel function exceeds 250MB?

Heavy dependencies (Puppeteer, LangChain, NumPy). Static assets bundled into the function instead of a CDN. Framework overhead you can't control (Next.js swc binaries,.next cache). Transitive dependencies you didn't know existed. Duplicate package copies in node_modules.

What are the concrete optimization techniques?

Use includeFiles and excludeFiles in vercel.json to exclude unneeded directories. Enable tree-shaking in your bundler to remove unused code. Audit dependencies with npm ls and yarn why; remove packages you aren't using. Offload static assets to external storage. For Next.js, enable outputFileTracing in next.config.js.

Can I increase the limit without optimizing?

Yes. Set VERCEL_SUPPORT_LARGE_FUNCTIONS=1 and enable Active CPU. This raises the limit to 5GB but changes your billing model from per-invocation to per-compute-second. Calculate your expected costs carefully; Fluid Compute is often more expensive than managed containers for heavy workloads.

What's the difference between the 250MB bundle limit and the 4.5MB payload limit?

250MB is your function's code and dependencies at deployment time (build-time limit). 4.5MB is the max size of a single HTTP request or response body at runtime (runtime limit). Both are hard limits; both need fixing if you hit them.

When should I stop optimizing and migrate instead?

When optimization requires removing core dependencies (Puppeteer, LangChain, heavy frameworks). When your app needs features containers provide (persistent processes, custom memory allocation, Docker flexibility). When you prefer predictable billing over paying per-use. When local debugging becomes harder than managing a container.

What containers platforms should I consider besides Ship?

Railway, Render, Northflank, and Fly.io are all solid managed container options. Evaluate based on pricing model (fixed vs. usage-based), feature set, and where your data needs to live. Ship is purpose-built for simplicity and flat pricing; others offer more advanced features but require more operational knowledge.

The Bottom Line

Vercel's 250MB serverless function limit isn't a bug; it's a design choice that makes cold starts fast and billing predictable. But if your app doesn't fit, you have a clear path: optimize aggressively if you can, try Fluid Compute if you need breathing room, or migrate to containers if optimization hits the wall. The GitHub community discussions show hundreds of developers cycling through frustration with this limit. You don't have to. Use the diagnostic tool, run the five optimizations, and make one decision: stay and pay, or move and simplify. Start with Ship if you choose containers; deployment takes minutes.

Deploy without size limits
Ship lets you containerize any app—no 250MB constraint, flat-fee billing, GDPR hosting available.
Get Started Free

Ready to self-host your own apps?

One server. Multiple apps. No per-app fees.

Get started →