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 size | What happens |
|---|---|
| Over 150 MiB | EAS CLI shows a warning; the build continues |
| Over 2 GiB | The 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 | headThe 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 ignoredExit 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.