Git & CI/CD Intermediate

Resolving GitHub Actions ‘Runner Out of Disk Space’ Error on Ubuntu 20.04 LTS

Troubleshoot and fix GitHub Actions 'out of disk space' errors on Ubuntu 20.04 LTS runners. Learn to free up cache, temporary files, and optimize your CI/CD workflows for reliable builds.

πŸ‘¨β€πŸ’»
Senior Systems Architect • Verified in Staging Labs

Troubleshoot and fix GitHub Actions 'out of disk space' errors on Ubuntu 20.04 LTS runners. Learn to free up cache, temporary files, and optimize your CI/CD workflows for reliable builds.

Introduction

Encountering a "No space left on device" or "out of disk space" error during a GitHub Actions workflow can be a frustrating roadblock in your CI/CD pipeline. This error indicates that the virtual machine (VM) runner provisioned by GitHub for your build or deployment job has exhausted its available disk storage. On ubuntu-20.04 runners, which typically provide a finite amount of disk space (often around 75GB total, but much of it pre-populated with tools), large repositories, extensive dependencies, or accumulating build artifacts can quickly fill up the root filesystem, leading to abrupt job failures.

This guide provides a highly technical, step-by-step approach to diagnose and resolve disk space issues in your GitHub Actions workflows running on Ubuntu 20.04 LTS.

Symptom & Error Signature

When your GitHub Actions workflow runs out of disk space, you will typically see error messages in the job logs that explicitly mention storage limitations. These can manifest during various operations, such as cloning repositories, installing dependencies, compiling code, or building Docker images.

Here are common error signatures you might encounter:

Run <your build or install command>
/usr/bin/docker-credential-helper-linux: No space left on device
##[error]The process '/usr/bin/docker' failed with exit code 1
##[error]There was an error when trying to connect to the Docker daemon: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

Another example during dependency installation:

mkdir: cannot create directory β€˜/home/runner/work/my-repo/my-repo/node_modules’: No space left on device
Error: Command failed with exit code 1: npm install

Or during package management operations:

E: <some_package_name>: Disk space running low
E: You don't have enough free space in /var/cache/apt/archives/.

Often, diagnostic steps may reveal the full disk:

df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay         75G   75G     0 100% /
tmpfs           64M     0   64M   0% /dev
tmpfs           7.8G     0  7.8G   0% /sys/fs/cgroup
shm             64M  12K   64M   1% /dev/shm
/dev/sda1       75G   75G     0 100% /etc/hosts
tmpfs           7.8G   12K  7.8G   1% /run/docker/libcontainerd/containerd/afae2...

Root Cause Analysis

The underlying reasons for GitHub Actions runners running out of disk space are typically one or a combination of the following:

  1. Accumulation of Large Dependencies: Projects with extensive dependency trees (e.g., node_modules in Node.js, large Python virtual environments, extensive Maven or Gradle caches, Go modules) can quickly consume gigabytes of space.
  2. Unmanaged Build Artifacts: Intermediate build products, compiler outputs, temporary files generated during compilation, or test reports that are not explicitly cleaned up can accumulate.
  3. Docker Image Bloat: Building Docker images, especially without proper multi-stage builds or pruning strategies, can leave numerous intermediate layers and unused images (dangling or none) consuming significant space. Each docker build command creates new layers.
  4. Inefficient Caching: While actions/cache is designed to save space and time, misconfigured keys or excessively large cached items can sometimes contribute to the problem, or simply the cache not being utilized for large dependency sets.
  5. GitHub-Hosted Runner Limitations: The default GitHub-hosted runners (like ubuntu-20.04) have a fixed amount of disk space. While generous for most projects, very large or complex builds may exceed these limits. The filesystem is an overlayfs on top of a base image, often with /dev/sda1 being the underlying device providing the capacity.
  6. Tooling and Environment Bloat: GitHub Actions runners come pre-installed with a vast array of tools. While optimized, some workflows might inadvertently duplicate or update these, or generate temporary files that aren't cleaned.

Step-by-Step Resolution

To resolve disk space issues, you need to systematically identify what's consuming space and implement strategies to clean up or prevent accumulation.

1. Diagnose Disk Usage During Workflow Execution

The first step is to pinpoint where the space is being consumed. Add diagnostic steps to your workflow before the failing job or critical operations.

jobs:
  build:
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Check initial disk space
        run: |
          echo "Initial disk space:"
          df -h
          echo "Top 20 largest directories in workspace:"
          sudo du -h --max-depth=1 /home/runner/work | sort -rh | head -n 20
          echo "Top 20 largest directories in /usr:"
          sudo du -h --max-depth=1 /usr | sort -rh | head -n 20
          echo "Top 20 largest directories in /var:"
          sudo du -h --max-depth=1 /var | sort -rh | head -n 20
        shell: bash

      # Your build steps follow here
      # ...

This diagnostic output will be visible in your workflow logs and help you understand which directories (/home/runner/work, /usr, /var, etc.) are consuming the most space, guiding your cleanup efforts.

2. Implement Aggressive Cleanup of Temporary Files and Caches

Target common locations for temporary files and caches. This should ideally be done before critical operations or as a separate cleanup job.

      - name: Perform System-Wide Cleanup
        run: |
          echo "Cleaning up APT caches..."
          sudo apt-get clean
          sudo rm -rf /var/lib/apt/lists/*
          echo "Cleaning up temporary directories..."
          sudo rm -rf /tmp/*
          echo "Removing common tooling caches/installs (if not needed for this workflow)..."
          # These directories are common large installations on GitHub runners.
          # REMOVE ONLY IF YOU ARE CERTAIN THEY ARE NOT NEEDED LATER IN THE JOB.
          # For example, if your project is pure Node.js, you might not need .NET or Android SDKs.
          sudo rm -rf /usr/share/dotnet # .NET SDKs
          sudo rm -rf /usr/local/lib/android # Android SDKs
          sudo rm -rf /opt/ghc # Haskell GHC
          sudo rm -rf "$AGENT_TOOLSDIRECTORY" # GitHub Actions agent tools directory

          echo "Disk space after system cleanup:"
          df -h
        shell: bash

      - name: Clean project-specific caches and dependencies
        if: always() # Ensure this always runs, even if earlier steps fail
        run: |
          echo "Cleaning Node.js caches and modules..."
          npm cache clean --force || true
          rm -rf node_modules || true
          yarn cache clean --force || true
          echo "Cleaning Python caches and virtual environments..."
          pip cache purge || true
          rm -rf .venv venv || true # Remove common Python virtual environment directories
          echo "Cleaning Java/Maven local repository..."
          rm -rf ~/.m2/repository || true
          echo "Cleaning Go module cache..."
          go clean -modcache || true

          echo "Disk space after project cleanup:"
          df -h
        shell: bash

The || true suffix in cleanup commands prevents the workflow from failing if the specified files or directories do not exist. This ensures cleanup steps don't introduce new failures.

Removing pre-installed tools like /usr/share/dotnet should only be done if you are absolutely certain your workflow does not depend on them. Misuse can lead to other build failures.

3. Optimize Docker Builds and Prune Docker Artifacts

Docker builds are a common culprit for disk space issues due to intermediate layers and unused images.

      - name: Free up Docker disk space
        # Run this before any Docker build steps or after they complete
        run: |
          echo "Pruning Docker system..."
          # docker system prune --all removes all stopped containers, all networks not used by at least one container,
          # all dangling images, and all build cache.
          # --volumes removes all unused volumes.
          sudo docker system prune --all --force --volumes
          echo "Disk space after Docker prune:"
          df -h
        shell: bash

      - name: Build and Push Docker Image
        run: |
          # Your docker build command
          # Example using BuildKit for better caching
          DOCKER_BUILDKIT=1 docker build --cache-from your-repo/your-image:latest -t your-repo/your-image:latest .
        shell: bash

docker system prune --all --force --volumes is an aggressive cleanup. While generally safe in GitHub Actions runners (which are ephemeral), ensure it doesn't remove anything critical if your workflow structure relies on specific Docker artifacts persisting across sequential steps within the same job. For a fresh job, it's highly recommended.

To prevent bloat during the build:

  • Use .dockerignore: Exclude unnecessary files (like node_modules, .git, temporary build directories) from your Docker build context.
  • Multi-stage builds: Reduce the size of your final Docker image by separating build-time dependencies from runtime dependencies.
  • Leverage BuildKit caching: Set DOCKER_BUILDKIT=1 and use --cache-from to reuse layers from previous builds, reducing re-downloading and re-building.

4. Effective Use of GitHub Actions Caching (actions/cache)

Properly caching dependencies is crucial for performance and can indirectly help with disk space by reducing repeated downloads, though the cache itself consumes space.

      - name: Cache Node.js modules
        uses: actions/cache@v4
        with:
          path: ~/.npm # Or ./node_modules, depending on your caching strategy
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
      - name: Install Node.js dependencies
        run: npm ci

      - name: Cache Python dependencies
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip # Or a specific venv directory
          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-
      - name: Install Python dependencies
        run: pip install -r requirements.txt

Choose your path wisely. Caching node_modules directly can be large. Caching the npm cache directory (~/.npm) might be more efficient for some projects. The key must accurately reflect changes in your dependency lock files to ensure cache invalidation when dependencies change.

5. Split Workflows or Jobs for Large Projects

If a single job performs too many operations or generates too many artifacts, consider splitting it into multiple jobs or even separate workflows. Each job in GitHub Actions runs on a fresh VM runner, ensuring a clean slate and separate disk space.

jobs:
  build_and_test:
    runs-on: ubuntu-20.04
    steps:
      # ... build and test code ...
      - name: Upload Build Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: my-app-build
          path: ./dist # Or your build output directory

  deploy:
    runs-on: ubuntu-20.04
    needs: build_and_test # This job will only run if build_and_test succeeds
    steps:
      - name: Download Build Artifacts
        uses: actions/download-artifact@v4
        with:
          name: my-app-build
          path: ./downloaded-build
      # ... deploy code using artifacts ...

This approach ensures that the disk space used by the build_and_test job is entirely discarded before the deploy job starts, preventing cumulative disk usage.

6. Utilize Larger Runners (GitHub Enterprise Cloud or Self-Hosted)

If all optimization efforts prove insufficient for GitHub-hosted runners, consider these options:

  • Larger GitHub-Hosted Runners (Enterprise Cloud): For GitHub Enterprise Cloud customers, larger runners are available (e.g., ubuntu-latest-4-core, ubuntu-latest-8-core, ubuntu-latest-16-core). These offer more CPU, memory, and crucially, more disk space.

    jobs:
      build:
        runs-on: ubuntu-latest-4-core # Requires GitHub Enterprise Cloud
        steps:
          # ...
    
  • Self-Hosted Runners: For ultimate control over resources, including disk space, deploy your own self-hosted runners. You provision and manage the underlying infrastructure (VMs or physical servers) which can have virtually unlimited disk space.

    jobs:
      build:
        runs-on: self-hosted # Requires a self-hosted runner configured with this label
        steps:
          # ...
    

    Using larger GitHub-hosted runners incurs additional costs. Evaluate this option carefully against the cost-effectiveness of optimizing your existing workflows. Self-hosted runners require significant operational overhead for maintenance, security, and scaling. Ensure your self-hosted runner machines have ample disk space and implement regular cleanup routines on them if artifacts are persistently stored.

πŸ‘¨β€πŸ’»

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

πŸ›‘οΈ

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.