◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 31 days left. If your app calls it, check the migration target●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 31 days left. If your app calls it, check the migration target●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/App Dev
◇ App Dev/2026-10-31Beginner

When EAS Build Stops at "Project archive is too big", Count What Gets Uploaded, Largest First

EAS Build warns at 150 MiB and fails at 2 GiB. Here is how to list what will be uploaded before you send it, find the culprit, and decide what goes into .easignore.

EAS Build25Expo234easignoreuploadtroubleshooting79

I was building a new wallpaper app with EAS for the first time. The terminal sat at "Compressing project files" for a long while, then printed a single red line.

Project archive is too big. Maximum allowed size is 2.0 GB.

The project had a few hundred source files. A 2 GB figure made no sense to me, and my stomach sank a little.

Here is the point I'd like to start with: this failure is rarely about your code being large. It happens because something you never meant to send is riding along. The cause usually sits at the edge of the project, not in src/.

There are two thresholds

According to Expo's project upload documentation, EAS CLI packs your project into one archive and uploads it. Size is handled like this.

Archive sizeWhat happens
Over 150 MiBEAS CLI shows a warning; the build continues
Over 2 GiBThe upload fails with the "too big" message above

The warning is the moment you can still turn back. I read mine as "the network is just slow", and that was the wrong turn.

node_modules is not part of the archive at all, since the build server reinstalls dependencies. So you can rule out "my dependencies are heavy" early.

Sort the upload by size before you send it

Every retry costs a full upload wait. As an indie developer, I'd rather spend that minute locally. This script makes git read exactly one ignore file and lists what would actually go in.

#!/bin/sh
# Local check: what would go into the archive, and what is heavy?
RULES=.gitignore
[ -f .easignore ] && RULES=.easignore   # .easignore replaces .gitignore when it exists
echo "Active rules: $RULES"
 
# -o = untracked, -c = tracked; --exclude-from reads only the one file you name
echo "--- largest first (KB) ---"
git ls-files -o -c --exclude-from="$RULES" -z | xargs -0 -r du -k | sort -rn | head -10
 
echo "--- total (bytes) ---"
git ls-files -o -c --exclude-from="$RULES" -z | xargs -0 -r du -cb | tail -1
 
echo "--- file count per directory ---"
git ls-files -o -c --exclude-from="$RULES" | awk -F/ '{print $1"/"$2}' | sort | uniq -c | sort -rn | head

The key is --exclude-from. It keeps stray .gitignore files in the working tree from interfering, so you see only what the current setup would pack.

I ran it on a small test folder whose .gitignore held only node_modules/. With a 3 MB and a 0.5 MB image under assets/original/, those two led the list. In other words, the top two or three lines usually tell you everything.

The weight usually comes from three places

What I actually found was always something I thought git was already ignoring.

  • Full-size source material (assets/original/, with high-resolution images or video from before delivery)
  • Leftovers from a local build (android/app/build/, ios/Pods/ and similar)
  • Half-finished exports (zip files, .psd, screen recordings)

With wallpaper apps the images are the product, and I had a habit of keeping the originals inside the project. The app ships a few dozen resized files, yet the folder held every original.

Why untracked files still go up

One easy misunderstanding: in the default workflow, where commits are not required, EAS CLI makes a shallow git clone and then copies your working directory into it. Uncommitted and untracked files are included unless something ignores them.

So "I never ran git add" is not protection. Full-size originals left untracked will still be sent unless .gitignore or .easignore excludes them. Setting cli.requireCommit to true in eas.json builds the archive from your last commit instead, but .easignore still applies there too.

When it's in .gitignore but still uploaded

Check which rule matches the path.

# Shows the rule that matches the path, or nothing if no rule matches
git check-ignore -v assets/original/wall_0001.png
echo "exit=$?"   # 0 = ignored, 1 = not ignored

Exit code 1 means the file is not ignored. A common cause is writing assets/original in .gitignore while the folder actually lives at app/assets/original. A leading / changes how a pattern anchors, so it is worth reading the path one segment at a time.

Deciding what goes into .easignore

Once you know the culprit, exclude it with .easignore. There is a catch: it does not add to .gitignore, it replaces it. Anything you forget to copy over, such as node_modules, goes back into the upload (I wrote about this in .easignore replaces .gitignore).

So copy your .gitignore first, then add the heavy items.

# Start by copying what .gitignore already says
node_modules/
.expo/
dist/
.env

# Then add what caused this failure and the build doesn't need
assets/original/
android/app/build/
ios/Pods/
*.psd
*.zip

Run the script again and confirm the total dropped. Files you'd rather not send at all, like .env, can be excluded in the same pass.

When a build feels heavy, count what's being packed before you suspect the code. I try to keep that order, especially when I'm in a hurry.

What to do next

Run the script once on your current project. If the total is above 150 MiB, take another look at .easignore before the next build. If nothing stands out, you've at least ruled the upload out.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◇ App Dev2026-10-14
App Review Says "Guideline 2.1, Blank Screen": Three Places to Look in an Expo Release Build
It opens fine in TestFlight, yet review rejects it for a blank screen. Here is the order I check things in an Expo + EAS release build: which environment holds your variables, secret visibility during config resolution, and what the reviewer's device does differently.
◇ App Dev2026-10-22
Expo SDK 58 With Stripe: pod install Stops, and Whether to Turn Off Precompiled Modules or Wait
useFrameworks is already dynamic, yet pod install says Stripe is static and the SDK 58 iOS build stops. The reported conditions, three ways out, and a small script to check your own project first.
◇ App Dev2026-10-15
When adjustsFontSizeToFit Shrinks Just One Row, Suspect the Box Height, Not the String
Only the 10th row of a list drops to 4pt on iOS, though the data and strings are fine. I read the Yoga rounding and font-fit sources, then counted how often the box comes up short in Node.