Git

Git Push Failing With "pack-objects Died" or Memory Error on Large Repos

· by DebuggedIt

Quick answer

"pack-objects died" during a push typically means Git ran out of memory while compressing objects for transmission — large repositories, especially those with...

"pack-objects died" during a push typically means Git ran out of memory while compressing objects for transmission — large repositories, especially those with large binary files or extensive history, can require significant memory during this packing step, and the fix generally involves either reducing memory pressure during the operation or reducing what actually needs to be packed.

The Problem

Pushing a large repository, or a large set of changes, fails partway through:

error: pack-objects died of signal 9
fatal: the remote end hung up unexpectedly
fatal: pack-objects died of memory error

Smaller pushes to the same repository work fine, pointing at the size of the specific push (or the repository's overall size) as the relevant factor.

Why It Happens

During a push, Git's pack-objects process compresses the objects being sent into an efficient pack file — this process needs to hold a meaningful amount of data in memory while it works, and can be genuinely memory-intensive for large repositories, particularly those with:

  • A very large number of objects accumulated across extensive commit history, even if any single commit's changes are individually modest
  • Large binary files (images, videos, compiled artifacts) committed directly into the repository, which are both memory-intensive to compress and generally poor candidates for storage directly in Git in the first place
  • A machine with genuinely limited available memory relative to what the specific push operation actually requires, particularly relevant on lower-resource CI runners or constrained local development environments

The Fix

First, reduce Git's memory usage during the pack operation by lowering the pack window size and memory limit settings, trading some compression efficiency for reduced peak memory usage:

git config pack.windowMemory "100m"
git config pack.packSizeLimit "100m"
git config pack.threads "1"

Reducing pack.threads to 1 specifically reduces peak memory usage by avoiding multiple simultaneous compression operations each consuming their own memory, at the cost of the packing process taking longer to complete — a reasonable tradeoff when memory, not time, is the actual constraint.

If the push includes a very large amount of new data at once (a large initial commit, a big batch of file additions), consider splitting it into multiple smaller commits and pushing incrementally, reducing the amount of data any single push operation needs to pack and transmit at once:

# Push in smaller increments rather than one enormous push
git push origin HEAD~20:refs/heads/main  # push up to 20 commits back first
git push origin main  # then push the remainder

If large binary files are a significant contributor to the repository's overall size and memory demands, consider migrating them to Git LFS (Large File Storage), which is specifically designed to handle large binary content more efficiently than Git's default object storage and packing approach:

git lfs install
git lfs track "*.psd" "*.mp4" "*.zip"
git add .gitattributes

Migrating existing large files already in history to LFS requires a more involved history rewrite (using a tool like git lfs migrate), but for new large files going forward, LFS tracking prevents them from continuing to add to this specific memory-pressure problem.

If you're on a memory-constrained machine (a lower-resource CI runner, a constrained local VM), and reducing Git's own memory settings alone isn't sufficient, consider whether temporarily increasing available memory (a larger CI runner tier, added swap space) for this specific operation is a practical, if not permanent, solution while addressing the underlying repository size concern separately.

Still Not Working?

If pack settings adjustments and incremental pushing both fail to resolve the issue for a genuinely very large repository, consider whether a shallow clone or partial clone approach is appropriate for your actual use case going forward, particularly for CI/CD environments that don't necessarily need the repository's full historical depth for every single operation:

git clone --depth 1 https://github.com/example/large-repo.git

This doesn't directly solve a push memory issue on an existing full clone, but for future clones and CI setups specifically, working with a shallow or partial clone reduces the overall object count Git needs to manage, which can meaningfully reduce memory pressure for various operations, including subsequent pushes performed from that reduced-depth clone rather than a full historical one.