Troubleshooting ‘Git error pack-objects died of signal 13 pipe break’ on Alpine Linux for Large Pushes

Resolve 'pack-objects died of signal 13 pipe break' during large Git pushes on Alpine Linux. This guide details causes like memory exhaustion and ulimits, offering step-by-step fixes.


Resolve 'pack-objects died of signal 13 pipe break' during large Git pushes on Alpine Linux. This guide details causes like memory exhaustion and ulimits, offering step-by-step fixes.

Introduction

Encountering a "Git error pack-objects died of signal 13 pipe break" during a large git push operation can be a frustrating experience, particularly on resource-constrained environments like Alpine Linux. This error signals a critical failure on the remote Git server, where the pack-objects process — responsible for compressing Git objects into an efficient packfile — terminates unexpectedly. The client, still attempting to write data, receives a SIGPIPE (signal 13), indicating that the pipe it's writing to has been closed by the reading end. This guide provides a deep dive into the root causes and offers a robust, step-by-step resolution tailored for Alpine Linux and common hosting environments.

Symptom & Error Signature

Users typically observe this error on their local terminal during a git push command, especially when pushing a large number of new objects, a substantial new branch, or a repository with significant history changes.

Client-Side Error Output:

$ git push origin main
Enumerating objects: 123456, done.
Counting objects: 100% (123456/123456), done.
Delta compression using up to 8 threads
Compressing objects: 100% (123450/123450), done.
Writing objects: 100% (123456/123456), 2.30 GiB | 10.00 MiB/s, done.
Total 123456 (delta 98765), reused 0 (delta 0), pack-reused 0
error: pack-objects died of signal 13
error: remote unpack failed: unpack-objects died with exit code 13
To ssh://your-git-server.com/repo.git
 ! [remote rejected] main -> main (pack-objects died)
error: failed to push some refs to 'ssh://your-git-server.com/repo.git'

Potential Server-Side Logs (e.g., in /var/log/messages, Docker logs, or journalctl):

While SIGPIPE is a client-side symptom, the root cause lies on the server. Server-side logs might reveal the actual termination reason for pack-objects:

kernel: [pid] Out of memory: Kill process [pid] (git) score [score] or sacrifice child
kernel: Killed process [pid] (git-pack-obje) total-vm:[KB]KB, anon-rss:[KB]KB, file-rss:[KB]KB, shmem-rss:[KB]KB

or

git-receive-pack: failed to write to stdout, errno=28 (No space left on device)

or for systemd units:

systemd[1]: git-receive-pack.service: Main process exited, code=killed, status=13/PIPE

Root Cause Analysis

The "pack-objects died of signal 13 pipe break" error primarily indicates that the Git pack-objects process on the remote server terminated prematurely while the client was still sending data. The SIGPIPE signal on the client is a direct consequence of the server-side process closing its end of the communication pipe. The most common underlying causes, especially on Alpine Linux known for its minimal footprint, are:

  1. Memory Exhaustion (Out-of-Memory Killer – OOM):

    • The pack-objects process can be very memory-intensive when dealing with large repositories, many objects, or significant delta compression.
    • Alpine Linux, often used in Docker containers or low-resource VMs, frequently has aggressive memory limits or simply less available RAM.
    • When pack-objects exceeds available memory or configured memory limits, the kernel's OOM killer steps in, terminating the process to prevent system instability. This is the most frequent cause of SIGPIPE during large pushes.
  2. Insufficient Disk Space:

    • The pack-objects process needs temporary disk space to create packfiles before they are moved into the repository's objects/pack directory.
    • If the server's disk, or the partition hosting the Git repository, runs out of space, pack-objects cannot complete its operation and exits, leading to a pipe break.
  3. Process Limits (ulimits):

    • Operating systems impose limits on processes, such as the number of open file descriptors (LimitNOFILE), the number of processes (LimitNPROC), or stack size (LimitSTACK).
    • For very large repositories with deep histories, pack-objects might internally open many files or spawn child processes. If it hits an ulimit (e.g., maximum open files), it can crash. This is particularly relevant in minimal environments or tightly configured systemd services or Docker containers.
  4. Network Timeouts/Instability (Less Common for SIGPIPE):

    • While SIGPIPE is generally a local process issue, an underlying network timeout or instability might cause the SSH session or HTTP connection to drop, which could lead to the server-side Git processes terminating prematurely. However, this usually manifests as different error codes or connection resets. For HTTP Git, web server (e.g., Nginx) or application server (e.g., Gunicorn, uWSGI) timeouts can also play a role.
  5. Corrupted Repository:

    • A rare but possible cause is a corrupted Git repository on the server. pack-objects might encounter an inconsistent object or packfile, leading to a crash.

Step-by-Step Resolution

This section outlines how to diagnose and resolve the pack-objects died of signal 13 error. We'll focus on common hosting practices, including Alpine-specific considerations where relevant, and general Linux system administration.

1. Server Resource Assessment (Disk Space & Memory)

Before making any configuration changes, verify that your server has adequate resources.

  1. Check Disk Space: Connect to your Alpine Linux server via SSH and check disk utilization, especially for the partition hosting your Git repositories.

    df -h
    

    Look for high usage (e.g., > 90%) on / (root), /var, or the specific mount point where your Git repositories reside. If disk space is critically low, free up space or expand the filesystem.

  2. Check Memory Usage: Review the server's current memory consumption.

    free -h
    cat /proc/meminfo | grep MemTotal
    

    If available memory is consistently low, or if the git process itself is observed consuming a large portion of RAM before crashing (you might need to use htop or top during a push attempt), then memory exhaustion is likely the culprit.

2. Increase Server Memory Limits

This is often the most effective solution for SIGPIPE related to pack-objects. The approach depends on how your Git server is deployed.

a. For Git over SSH (Standalone Server/VM)

If Git is accessed via SSH, the sshd daemon might be constrained, or the system itself is running low on memory.

  • System-wide Memory: Consider increasing the RAM allocated to your VM or physical server if it's consistently running low.

  • SSH Daemon Limits (less common for direct OOM, but good practice): While Alpine's sshd typically uses OpenRC or is standalone, systemd environments (like Ubuntu/Debian) allow setting limits for services.

    If your Git server runs within a systemd service unit (e.g., a custom Git daemon or Git web service), you can define memory limits there. For systemd on Ubuntu/Debian:

    # Example: /etc/systemd/system/your-git-service.service.d/limits.conf
    [Service]
    MemoryLimit=2G # Or higher, e.g., 4G. Adjust based on your server's total RAM.
    

    Then reload and restart the service:

    sudo systemctl daemon-reload
    sudo systemctl restart your-git-service.service
    
b. For Git in Docker Containers (Alpine within Docker)

If your Git server (e.g., Gitea, GitLab, or a bare Git repository served via SSH/HTTP) runs inside a Docker container based on Alpine Linux, you must adjust the container's resource limits.

# When running the container:
docker run -d 
  --name git_server 
  --memory="4g"            # Allocate 4GB of RAM
  --memory-swap="8g"       # Allow 8GB of swap (if available on host)
  --memory-swappiness="0"  # Prefer not to swap (reduces I/O if possible)
  your_alpine_git_image:latest

If using Docker Compose, update your docker-compose.yml:

version: '3.8'
services:
  git_server:
    image: your_alpine_git_image:latest
    container_name: git_server
    deploy:
      resources:
        limits:
          memory: 4g # Set memory limit to 4GB
        reservations:
          memory: 2g # Reserve at least 2GB

After modifying, run docker-compose up -d --force-recreate.

c. For Git over HTTP (Nginx + Git Smart HTTP)

If you're serving Git over HTTP/HTTPS using Nginx and a backend like git-http-backend (CGI/FastCGI) or a dedicated Git application (like Gitea, GitLab), also check these:

  • PHP-FPM (if using PHP-based Git web frontends): Increase memory_limit in php.ini.

    # Example: /etc/php8/php.ini (for Alpine), or /etc/php/<version>/fpm/php.ini (Ubuntu/Debian)
    memory_limit = 512M # Or 1G, 2G. Adjust as needed.
    

    Restart PHP-FPM service:

    # On Alpine (OpenRC)
    rc-service php-fpm8 restart
    # On Ubuntu/Debian (Systemd)
    sudo systemctl restart php8.2-fpm.service # Adjust version
    
  • Nginx Client Max Body Size: While pack-objects is internal, large HTTP pushes can also fail if Nginx prematurely closes the connection. Ensure Nginx allows large body sizes.

    # Example: In http block, server block, or location block
    client_max_body_size 0; # Allows unlimited body size
    client_body_buffer_size 128k;
    

    Reload Nginx configuration:

    sudo nginx -t && sudo systemctl reload nginx # For Systemd systems
    # On Alpine (OpenRC)
    sudo nginx -t && rc-service nginx reload
    

3. Adjust Process ulimits (Open Files, Processes)

Git operations, especially on large repositories, can hit default operating system limits for open files or processes.

a. System-wide ulimits (via /etc/security/limits.conf)

For bare-metal Alpine or VMs, edit /etc/security/limits.conf. This method requires PAM (Pluggable Authentication Modules) to be active for SSH logins, which is common.

# Example: /etc/security/limits.conf
# <domain>      <type>  <item>         <value>
*               soft    nofile         65536
*               hard    nofile         65536
*               soft    nproc          4096
*               hard    nproc          4096

Log out and log back in to apply these limits for new sessions.

On minimal Alpine installations, PAM might not be fully configured to process limits.conf for all services. If this doesn't work, consider systemd unit limits or Docker --ulimit.

b. systemd Service Unit Limits

If your Git service (e.g., an SSH server configured to restrict users, or a custom Git daemon) is managed by systemd (common on Ubuntu/Debian, less so by default on Alpine but possible if installed), you can set limits directly in its service unit file.

# Example: /etc/systemd/system/sshd.service.d/limits.conf (or your custom service)
[Service]
LimitNOFILE=65536
LimitNPROC=4096

Reload systemd and restart the service:

sudo systemctl daemon-reload
sudo systemctl restart sshd.service # Or your Git service
c. Docker Container ulimits

For Git servers running in Docker containers, explicitly set ulimits using the --ulimit flag during docker run or in docker-compose.yml.

# Docker run example:
docker run -d 
  --name git_server 
  --ulimit nofile=65536:65536  # soft:hard limit for open files
  --ulimit nproc=4096:4096     # soft:hard limit for processes
  your_alpine_git_image:latest

For Docker Compose:

version: '3.8'
services:
  git_server:
    image: your_alpine_git_image:latest
    container_name: git_server
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
      nproc:
        soft: 4096
        hard: 4096

After modifying, run docker-compose up -d --force-recreate.

4. Optimize Git Repository (Server-Side)

Sometimes, the repository itself can become inefficient, causing pack-objects to work harder.

  1. Run git gc: Perform garbage collection on the remote repository. This optimizes the repository by packing loose objects and removing old packfiles.

    Connect to your server, navigate to the bare Git repository (e.g., /srv/git/repo.git), and run:

    cd /srv/git/repo.git
    git gc --prune=now
    

    This can be a memory-intensive operation itself, so ensure resources are available before running it.

  2. Git Server Configuration: While less common, certain Git configurations on the server can impact pack-objects.

    cd /srv/git/repo.git
    git config pack.windowMemory "256m" # Increase window memory if needed
    git config pack.SizeLimit "4g"      # Limit packfile size, might make more smaller packfiles
    

5. Network & Timeout Considerations (for HTTP Git)

While less direct for SIGPIPE, ensure your network infrastructure and HTTP server are not prematurely closing connections for large pushes.

  • SSH ClientAliveInterval: For SSH Git, if the client connection is idle for too long (e.g., during a very slow compression phase), the SSH server might disconnect it.

    # Example: /etc/ssh/sshd_config
    ClientAliveInterval 300  # Sends null packets every 300 seconds (5 min)
    ClientAliveCountMax 3    # Allows 3 intervals without client response before disconnecting
    

    Restart sshd after changes: sudo systemctl restart sshd (Ubuntu/Debian) or rc-service sshd restart (Alpine).

  • Nginx/Proxy Timeouts: For HTTP Git, ensure Nginx or any reverse proxy has generous timeout settings.

    # Example: In http, server, or location block
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;
    send_timeout 300s;
    

6. Client-Side Workarounds (Temporary or When Server Control is Limited)

If you cannot immediately modify the server, consider these client-side adjustments:

  1. Split Large Pushes: If you're pushing a very large branch or many new commits, try to split it into smaller, more manageable pushes. This might involve pushing a few commits at a time or force-pushing smaller segments if your workflow allows.

  2. Increase http.postBuffer (for HTTP Git only): This client-side setting controls the buffer size Git uses for HTTP POST requests. Increasing it can help for large HTTP pushes.

    git config --global http.postBuffer 524288000 # 500 MB
    

7. Advanced Diagnostics: strace (Highly Technical)

If the problem persists and you have root access to the server, strace can provide granular insight into the system calls made by pack-objects right before it crashes.

  1. Identify the Git Process: When a git push is initiated, on the server, an sshd or web server process will spawn git-receive-pack, which in turn spawns git-pack-objects. You need to attach strace to git-receive-pack or its child git-pack-objects.

  2. Attach strace:

    • Start a git push from your client.
    • Immediately on the server, find the git-receive-pack or git-pack-objects process ID (PID):
      ps aux | grep git-receive-pack
      ps aux | grep git-pack-objects
      
    • Attach strace to it:
      sudo strace -fp <PID> -o /tmp/git_strace.log
      
      Replace <PID> with the actual process ID.
    • Let the client git push fail.
    • Examine /tmp/git_strace.log for system call failures (e.g., ENOMEM for out-of-memory, EAGAIN for resource limits, EPIPE for pipe errors) or the last few successful calls before termination. This log can pinpoint the exact system call that failed.

strace can be resource-intensive and generate very large log files. Use it carefully on production systems and remove log files after debugging.

By systematically working through these steps, you should be able to identify and resolve the underlying resource limitations or configuration issues causing the "Git error pack-objects died of signal 13 pipe break" on your Alpine Linux-based Git server.