Artifact Storage Management

Added: 2026-01-24 Status: Active in production PR: #477


Overview

GitHub Actions artifact storage has quota limits that can cause CI failures. This system proactively manages storage to prevent quota exhaustion.

Key Components:

  1. Scheduled cleanup workflow - Deletes old artifacts weekly
  2. Non-blocking uploads - CI continues even if upload fails
  3. Self-hosted runner migration - Reduces quota pressure

The Problem

GitHub artifact storage has several constraints:

TierStorage QuotaRetention
Free500 MB90 days
Pro2 GB90 days

Critical Issue: After deleting artifacts, quota recalculation is delayed 6-12 hours. During this window, CI can still fail with quota exhaustion errors even though artifacts were deleted.

Symptoms of quota exhaustion:

Error: Not enough artifact storage quota available

Solution 1: Artifact Cleanup Workflow

File: .github/workflows/artifact-cleanup.yml

Scheduled Execution

Runs automatically every Sunday at 3 AM UTC to delete artifacts older than 3 days.

on:
  schedule:
    - cron: '0 3 * * 0'  # Every Sunday at 3 AM UTC

Manual Trigger

Can be triggered manually with configurable options:

  • max_age_days: Delete artifacts older than N days (default: 3)
  • dry_run: Preview what would be deleted without actually deleting

To run manually:

  1. Go to Actions tab
  2. Select “Artifact Cleanup” workflow
  3. Click “Run workflow”
  4. Set options and run

How It Works

// Lists all artifacts and deletes those older than cutoff
const cutoffDate = new Date(Date.now() - maxAgeDays * 24 * 60 * 60 * 1000);
 
for (const artifact of artifacts) {
  if (createdAt < cutoffDate) {
    await github.rest.actions.deleteArtifact({
      owner: context.repo.owner,
      repo: context.repo.repo,
      artifact_id: artifact.id
    });
  }
}

Required Permissions

The workflow requires actions: write permission to delete artifacts:

permissions:
  actions: write  # Required to delete artifacts

Solution 2: Non-Blocking Artifact Uploads

All artifact upload steps now include continue-on-error: true to prevent CI failures when quota is exhausted.

Before (Blocking)

- name: Upload test reports
  uses: actions/upload-artifact@v4
  with:
    name: test-reports
    path: build/reports/

CI fails if upload fails.

After (Non-Blocking)

- name: Upload test reports
  uses: actions/upload-artifact@v4
  continue-on-error: true  # Don't fail CI if upload fails
  with:
    name: test-reports
    path: build/reports/

CI continues even if upload fails. The upload failure is logged but doesn’t block the workflow.

Affected Workflows

WorkflowArtifacts Made Non-Blocking
android-ci.ymlLint reports, JaCoCo coverage, test reports, OWASP reports
ios-ci.ymlUnit test results, UI test results
android-integration-tests.ymlIntegration test results, UI test results

Solution 3: Self-Hosted Runner Migration

PR #477 also migrated android-integration-tests.yml to use self-hosted runners by default. This reduces GitHub-hosted runner quota consumption.

Key Changes

  1. Added decide_runner job - Platform-conditional runner selection
  2. Made KVM setup Linux-only - Software rendering on Windows/macOS
  3. Added [github] escape hatch - Force GitHub-hosted if needed

Runner Selection Logic

decide_runner:
  runs-on: self-hosted  # Decision runs on self-hosted (no quota)
  outputs:
    runner_label: ${{ steps.decide.outputs.runner_label }}

Jobs queue when self-hosted runners are offline instead of auto-fallback.


Monitoring

Check Current Storage Usage

  1. Go to Repository Settings > Actions > General
  2. Look for “Artifact and log storage used”

Check Cleanup Results

After cleanup runs, check the workflow summary for:

  • Number of artifacts deleted
  • Total storage freed (in MB)

Emergency Cleanup

If CI is failing due to quota:

  1. Manually trigger cleanup:

    • Go to Actions > Artifact Cleanup
    • Run with max_age_days: 1 for aggressive cleanup
  2. Wait 6-12 hours for quota to recalculate

  3. Use [self-hosted] flag in commit messages to bypass GitHub-hosted runners temporarily


Best Practices

Do’s

  • Keep retention low (3 days is usually sufficient)
  • Schedule cleanup during low-activity hours (weekends)
  • Use dry-run before aggressive cleanup
  • Monitor quota weekly (see Maintenance Tasks)

Don’ts

  • Don’t rely on immediate quota recalculation
  • Don’t upload large binary artifacts (APKs, IPAs)
  • Don’t keep artifacts for weeks/months
  • Don’t skip continue-on-error on artifact uploads


Tags: ci-cd github-actions artifacts quota-management Status: Active in production