Die Währung von GitHub Actions sind Minutes – ein Begriff, der irreführend einfach klingt. Eine Minute auf einem Linux-Runner ist tatsächlich eine Minute Rechenzeit. Auf einem macOS-Runner hingegen konsumiert dieselbe 60-Sekunden-Ausführung zehn Minutes aus dem Kontingent. Diese Multiplier-Logik verwandelt die Kostenrechnung in eine mehrdimensionale Gleichung, bei der Betriebssystem, Parallelität und Workflow-Dauer zusammenwirken.
Hinzu kommt ein differenziertes System aus Freibetragen, Limits und Abrechnungsregeln, das sich je nach GitHub-Plan unterscheidet. Public Repositories erhalten unlimitierte kostenlose Ausführungszeit, private Repositories hingegen unterliegen strikten Quotas. Wer diese Mechanismen nicht durchschaut, erlebt beim Monatsabschluss böse Überraschungen – besonders dann, wenn Matrix-Builds mit mehreren Betriebssystemen zum Einsatz kommen.
GitHub unterscheidet drei Runner-Typen mit unterschiedlichen Abrechnungsfaktoren:
| Runner-Typ | Multiplier | Effektive Kosten pro Echtzeit-Minute | Minute bei 60s Ausführung |
|---|---|---|---|
| Linux (Ubuntu) | 1x | $0.008 | 1 Minute |
| Windows | 2x | $0.016 | 2 Minutes |
| macOS | 10x | $0.080 | 10 Minutes |
Ein Workflow, der auf allen drei Betriebssystemen jeweils fünf Minuten läuft, verbraucht nicht etwa 15 Minutes, sondern:
Linux: 5 Minuten × 1 = 5 Minutes
Windows: 5 Minuten × 2 = 10 Minutes
macOS: 5 Minuten × 10 = 50 Minutes
──────────────────────────────────────
Gesamt: 65 Minutes
Bei einem Free-Account mit 2.000 inkludierten Minutes pro Monat wären nach 31 solcher Workflow-Runs bereits 2.015 Minutes verbraucht – das Budget ist erschöpft.
Die Multiplier reflektieren die unterschiedlichen Infrastrukturkosten: Linux-Runner nutzen günstige x86-Hardware, macOS-Runner hingegen benötigen teure Mac-Hardware mit speziellen Lizenzanforderungen. Windows liegt preislich dazwischen, teilweise durch Lizenzkosten bedingt.
Die Abrechnung erfolgt auf volle Minuten aufgerundet. Ein Job, der 3 Minuten und 12 Sekunden läuft, wird als 4 Minuten berechnet. Bei macOS bedeutet das: 4 Minuten Laufzeit = 40 verbrauchte Minutes. Diese Rundungslogik summiert sich schnell bei vielen kurzen Jobs.
Ein typisches Anti-Pattern: Ein Entwickler richtet einen Test-Workflow auf macOS ein, der bei jedem Push läuft. Der Workflow dauert nur 2 Minuten, läuft aber 50-mal täglich. Das ergibt 100 Echtzeit-Minuten pro Tag, aber 1.000 verbrauchte Minutes – die Hälfte des Free-Kontingents für einen einzigen Workflow.
Die inkludierten Minutes variieren erheblich zwischen den GitHub-Plänen:
| Plan | Minutes/Monat | Storage | Cache | Preis |
|---|---|---|---|---|
| Free | 2.000 | 500 MB | 10 GB/Repo | $0 |
| Pro | 3.000 | 1 GB | 10 GB/Repo | $4 |
| Team | 3.000 | 2 GB | 10 GB/Repo | $4/User |
| Enterprise Cloud | 50.000 | 50 GB | 10 GB/Repo | Custom |
Wichtig: Diese Kontingente gelten pro Account, nicht pro Repository. Ein User mit zehn privaten Repositories teilt sich 2.000 Minutes über alle Repositories hinweg. Das kann problematisch werden, wenn ein Repository intensiv CI/CD betreibt und die anderen dadurch ausgehungert werden.
Die Minutes werden monatlich auf Null zurückgesetzt. Ungenutzte Minutes verfallen – es gibt kein Roll-over. Ein strategischer Fehler ist deshalb, am Monatsanfang sparsam zu sein und am Monatsende großzügig zu builden. Besser: Gleichmäßige Verteilung über den Monat.
Public Repositories haben eine Sonderstellung: Sie erhalten
unlimitierte kostenlose Minutes für Standard-Runner. Dies gilt jedoch
nicht für Larger Runner (4+ vCPUs), die auch bei Public Repos
kostenpflichtig sind. Ein Open-Source-Projekt kann also kostenlos auf
ubuntu-latest bauen, zahlt aber für
ubuntu-latest-4-cores.
Überschreitet ein Account sein Kontingent, blockiert GitHub weitere Workflow-Runs – es sei denn, eine Zahlungsmethode ist hinterlegt. Dann beginnt die Abrechnung zusätzlicher Minutes zu den oben genannten Per-Minute-Raten. Ohne Zahlungsmethode laufen keine Workflows mehr, bis der nächste Monat beginnt.
GitHub implementiert ein Spending-Limit-System, um unerwartete Rechnungen zu verhindern. Standardmäßig ist das Limit auf $0 gesetzt – sobald die inkludierten Minutes aufgebraucht sind, stoppen alle Workflows.
Diese harte Grenze lässt sich unter Settings →
Billing and plans → Spending limits anpassen.
Werte zwischen $0 und unbegrenzt sind möglich. Ein Limit von
beispielsweise $50 erlaubt Workflows, bis zusätzliche Kosten von $50
entstanden sind, dann stoppt die Ausführung wieder.
Ein praktisches Szenario: Ein Startup mit Team-Plan (3.000 inkludierte Minutes) setzt ein Spending Limit von $100. Die Minutes reichen für normale Entwicklung, aber intensive Feature-Branches mit vielen Matrix-Builds können das Budget überschreiten. Das $100-Limit gibt einen Puffer von:
$100 ÷ $0.008 = 12.500 zusätzliche Linux-Minutes
oder
$100 ÷ $0.016 = 6.250 zusätzliche Windows-Minutes
oder
$100 ÷ $0.080 = 1.250 zusätzliche macOS-Minutes
Ein häufiges Missverständnis: Das Spending Limit gilt pro Abrechnungsperiode, nicht pro Workflow oder Repository. Mehrere Repositories können das gemeinsame Limit aufbrauchen.
Für Organisationen existiert eine zusätzliche E-Mail-Benachrichtigung bei 75%, 90% und 100% Kontingentverbrauch. Diese Schwellenwerte sind nicht konfigurierbar, geben aber rechtzeitig Warnung vor einer Workflow-Blockade.
Matrix-Strategien generieren multiple Jobs aus einer einzigen Definition. Ein simples Beispiel:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [16, 18, 20]Dieser Matrix-Build erzeugt 3 × 3 = 9 Jobs. Läuft jeder Job 10 Minuten, entstehen:
Ubuntu: 3 Jobs × 10 Min × 1 = 30 Minutes
Windows: 3 Jobs × 10 Min × 2 = 60 Minutes
macOS: 3 Jobs × 10 Min × 10 = 300 Minutes
────────────────────────────────────────
Gesamt: 390 Minutes
Ein einzelner Workflow-Run verbraucht fast 20% des Free-Kontingents. Bei fünf Runs pro Tag (durchaus realistisch bei aktivem Development) sind 2.000 Minutes innerhalb einer Woche aufgebraucht.
Die Kosten skalieren nicht linear, sondern multiplikativ. Erweitert man die Matrix um Python-Versionen:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [16, 18, 20]
python: [3.9, 3.10, 3.11]Jetzt entstehen 3 × 3 × 3 = 27 Jobs. Die Minutes-Rechnung explodiert:
27 Jobs, davon:
- 9 auf Ubuntu: 9 × 10 × 1 = 90 Minutes
- 9 auf Windows: 9 × 10 × 2 = 180 Minutes
- 9 auf macOS: 9 × 10 × 10 = 900 Minutes
─────────────────────────────────────────
Gesamt: 1.170 Minutes
Ein einzelner Run verbraucht über die Hälfte des Free-Kontingents. Das ist die exponentielle Falle von Multi-Dimensional-Matrices.
Die Lösung liegt in gezielter Matrix-Reduktion. Statt alle Kombinationen zu testen, fokussiert man auf relevante:
strategy:
matrix:
include:
- os: ubuntu-latest
node: 20
python: 3.11
- os: windows-latest
node: 18
python: 3.10
- os: macos-latest
node: 16
python: 3.9Dies erzeugt nur 3 Jobs statt 27 – eine 90%-Reduktion. Die Abdeckung bleibt ausreichend: Jedes OS, jede Node-Version und jede Python-Version wird getestet, nur nicht in jeder Kombination.
Eine weitere Strategie: macOS-Tests auf kritische Workflows beschränken. Pull-Request-Checks laufen nur auf Linux, Nightly-Builds zusätzlich auf Windows und macOS. Dies balanciert Kosteneffizienz mit Testabdeckung.
Die Minutes sind nur eine Dimension der Kostenkontrolle. Concurrency-Limits beschränken, wie viele Jobs gleichzeitig laufen können:
| Plan | Standard Runner | macOS | Larger Runner |
|---|---|---|---|
| Free | 20 | 5 | N/A |
| Pro | 40 | 5 | N/A |
| Team | 60 | 5 | 1.000 |
| Enterprise | 500 | 50 | 1.000 |
Diese Limits sind Account-weit. Ein Free-Account kann maximal 20 Jobs parallel ausführen, unabhängig von der Anzahl Repositories oder Workflows.
Ein 27-Jobs-Matrix-Build trifft auf das 20-Jobs-Limit eines Free-Accounts: 20 Jobs starten sofort, 7 warten in der Queue. Sobald ein Job endet, startet der nächste. Die Gesamtlaufzeit verlängert sich dadurch:
Ohne Limit: 27 Jobs parallel = 10 Minuten
Mit Limit: 20 + 7 sequenziell = ~14 Minuten
Die Wartezeit zählt nicht als verbrauchte Minutes – GitHub berechnet nur tatsächliche Ausführungszeit. Dennoch entstehen indirekte Kosten durch verzögerte Feedback-Zyklen.
macOS hat zusätzlich ein dediziertes Limit von 5 concurrent Jobs (außer Enterprise mit 50). Ein großer Matrix-Build mit 9 macOS-Jobs kann also nur 5 parallel ausführen, die restlichen 4 müssen warten. Dies verlängert die Durchlaufzeit dramatisch, wenn macOS der Flaschenhals ist.
Larger Runner haben separate Limits (Team/Enterprise: 1.000). Ein Workaround für Concurrency-Bottlenecks ist daher der Umstieg auf Larger Runner – allerdings zu höheren Kosten, da inkludierte Minutes dort nicht gelten.
Die Concurrency kann auch absichtlich limitiert werden, etwa für Deployment-Workflows:
concurrency:
group: production-deploy
cancel-in-progress: falseDies garantiert, dass nur ein Deployment gleichzeitig läuft, unabhängig vom Account-Limit. Nützlich, um Race Conditions bei Deployments zu vermeiden, hat aber keine direkten Kostenauswirkungen.
Neben Minutes fallen Kosten für Storage an. GitHub unterscheidet zwei Arten:
Artifacts sind Build-Outputs: Kompilierte Binaries, Test-Reports, Coverage-Daten. Die Retention-Periode ist konfigurierbar (1-400 Tage je nach Plan), Standard sind 90 Tage. Storage-Kosten: $0.25/GB pro Monat.
Caches sind Zwischenspeicher für Dependencies:
node_modules, Maven-Repositories, Docker-Layer. Pro
Repository sind 10 GB kostenlos, darüber hinaus $0.07/GiB pro Monat.
Ein typisches Node.js-Projekt cachet node_modules mit
~500 MB. Bei 10 aktiven Feature-Branches entstehen 5 GB Cache-Usage –
innerhalb des Freibetrags. Kommen weitere 20 Branches hinzu, wächst der
Cache auf 15 GB. Die zusätzlichen 5 GB kosten $0.35 pro Monat –
vernachlässigbar.
Artifacts hingegen akkumulieren schnell: Ein Build erzeugt 200 MB Artifacts (Binary + Source Maps + Tests). Bei täglichen Builds sind das 6 GB pro Monat. Nach 90 Tagen Retention: 180 GB gespeichert = $45/Monat nur für Artifacts.
Die Lösung: Aggressive Retention-Policies. Artifacts für Pull Requests benötigen nur 7-Tage-Retention, nicht 90. Produktions-Releases hingegen sollten länger aufbewahrt werden. Dies konfiguriert man per Workflow:
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 7Für Cache existiert eine automatische LRU-Eviction: Wird das 10-GB-Limit erreicht, löscht GitHub die am längsten ungenutzten Caches. Ein inaktiver Feature-Branch verliert seinen Cache nach ~7 Tagen Inaktivität (konfigurierbar bis 365 Tage).
Ein optimales Cache-Management nutzt key und
restore-keys intelligent:
- uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-Dies erstellt einen Cache pro OS und Lock-File-Hash. Bei
unverändertem package-lock.json wird der Cache
wiederverwendet, bei Änderung ein neuer erstellt. Der alte Cache wird
durch LRU-Eviction automatisch entfernt, wenn das Limit erreicht
wird.
Ein mittelgroßes Team (5 Entwickler, 10 private Repositories, Team-Plan) hat folgende Nutzung:
Kostenrechnung zusätzlicher Minutes:
Linux-Anteil (geschätzt 40%): 1.580 × $0.008 = $12.64
Windows-Anteil (geschätzt 20%): 790 × $0.016 = $12.64
macOS-Anteil (geschätzt 40%): 1.580 × $0.080 = $126.40
────────────────────────────────────────────────────
Gesamt: $151.68
Hinzu kommen Storage-Kosten:
Artifacts: 120 GB × $0.25 = $30.00
Cache: 15 GB zusätzlich × $0.07 = $1.05
────────────────────────────────────────────
Gesamt: $31.05
Gesamtkosten: Team-Plan ($4 × 5 = $20) + Minutes ($151.68) + Storage ($31.05) = $202.73 pro Monat.
Optimierungsmaßnahmen könnten sein:
Nach Optimierung: $74.73 pro Monat – eine 64%-Reduktion.
Die Kernlektion: Minutes-Kosten sind beherrschbar, wenn man bewusst
plant. Die teuersten Fehler sind unkontrollierte macOS-Matrix-Builds und
lange Artifact-Retention ohne Notwendigkeit. Ein regelmäßiger Blick auf
das Billing-Dashboard unter Settings →
Billing and plans → Usage this month zeigt, wo
die Minutes hinfließen – und wo Optimierungspotential liegt.