TL;DR: GitHub Actions ist das eingebaute CI/CD-System von GitHub. Du definierst Workflows als YAML-Dateien, die bei Push, Pull Request oder nach Zeitplan laufen. In diesem Artikel zeige ich dir alles: von deinem ersten Workflow über Matrix-Builds und Docker-Images bis hin zu Self-Hosted Runnern und wiederverwendbaren Workflows.
Stell dir vor, du pushst Code und alles passiert automatisch: Linting, Tests, Build, Deployment. Kein manuelles Rumgefummel mehr. Klingt gut? Genau das macht CI/CD — und GitHub Actions ist der einfachste Weg dahin.
🤔 Was ist CI/CD und warum sollte es dich interessieren?
CI (Continuous Integration) bedeutet, dass dein Code bei jedem Push automatisch getestet wird. CD (Continuous Deployment/Delivery) sorgt dafür, dass getesteter Code automatisch deployed wird. Zusammen bilden sie das Rückgrat moderner Softwareentwicklung.
Ohne CI/CD passiert Folgendes: Du vergisst Tests auszuführen. Ein Kollege merged kaputten Code. Das Deployment ist ein Freitagnachmittag-Albtraum. Mit CI/CD? Push, lehne dich zurück, fertig.
GitHub Actions ist direkt in GitHub integriert — kein externer Service, kein zusätzliches Setup. Du brauchst nur eine YAML-Datei in deinem Repository.
🧩 Die Grundkonzepte: Workflows, Jobs, Steps, Runner
Bevor wir loslegen, ein kurzer Überblick über die Bausteine:
- Workflow: Eine YAML-Datei unter
.github/workflows/. Definiert, wann und was passiert. - Job: Eine Gruppe von Schritten, die auf demselben Runner laufen. Jobs können parallel oder sequenziell laufen.
- Step: Ein einzelner Befehl oder eine Action. Steps laufen nacheinander innerhalb eines Jobs.
- Runner: Die Maschine, auf der dein Job ausgeführt wird. GitHub stellt kostenlose Runner bereit (Ubuntu, Windows, macOS) — oder du nutzt deine eigenen.
- Action: Ein wiederverwendbarer Baustein. Tausende davon findest du im GitHub Marketplace.
🚀 Dein erster Workflow: Lint und Test bei Push/PR
Fangen wir einfach an. Erstelle die Datei .github/workflows/ci.yml in deinem Repository:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Test
run: npm testDas war's. Bei jedem Push auf main und bei jedem Pull Request werden Linting und Tests automatisch ausgeführt. Der grüne Haken oder das rote X siehst du direkt im PR.
🔢 Matrix-Builds: Teste auf mehreren Node.js-Versionen
Du willst sicherstellen, dass dein Code auf verschiedenen Node.js-Versionen läuft? Matrix-Builds machen das trivial:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm testGitHub erstellt automatisch drei parallele Jobs — einen für jede Version. Du kannst die Matrix auch für verschiedene Betriebssysteme erweitern:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20]
runs-on: ${{ matrix.os }}Das gibt dir 6 Kombinationen. Automatisch. Ohne Copy-Paste.
⚡ Caching: Schnellere Builds durch Zwischenspeicherung
Jedes Mal npm ci laufen zu lassen dauert. Mit Caching sparst du wertvolle Minuten:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ciDie setup-node Action hat Caching direkt eingebaut. Alternativ kannst du den generischen Cache verwenden:
- name: Cache node_modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-Erster Build? Normal langsam. Jeder weitere? Deutlich schneller. Bei großen Projekten spart das locker 2-3 Minuten pro Run.
🔐 Secrets und Umgebungsvariablen
API-Keys, Tokens, Passwörter — die gehören nicht in deinen Code. GitHub Secrets sind dafür da:
# Secrets über die GitHub CLI setzen
gh secret set MY_API_KEY --body "super-secret-value"
gh secret set DOCKER_PASSWORD --body "my-docker-password"Im Workflow greifst du so darauf zu:
env:
API_URL: https://api.example.com
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy
env:
API_KEY: ${{ secrets.MY_API_KEY }}
run: |
curl -H "Authorization: Bearer $API_KEY" $API_URL/deployWichtig: Secrets werden in Logs automatisch maskiert. Du siehst nur *** statt des eigentlichen Werts. Und in Pull Requests von Forks sind Secrets nicht verfügbar — ein wichtiger Sicherheitsmechanismus.
🐳 Docker-Images bauen und pushen
Docker und GitHub Actions sind ein Dreamteam. So baust und pushst du ein Image zu Docker Hub oder GitHub Container Registry:
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}Das Schöne: GITHUB_TOKEN wird automatisch bereitgestellt. Kein extra Secret nötig für die GitHub Container Registry.
🌐 Deployment: Vercel, Netlify oder per SSH
Der Build ist durch, die Tests sind grün — jetzt ab auf den Server.
Vercel:
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'SSH auf einen eigenen Server:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/myapp
git pull origin main
npm ci --production
pm2 restart myappEgal ob PaaS oder Bare Metal — für alles gibt es eine passende Action.
♻️ Wiederverwendbare Workflows und Composite Actions
Copy-Paste über mehrere Repos hinweg? Nein danke. Wiederverwendbare Workflows lösen das elegant:
# .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: '20'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
- run: npm testAufrufen kannst du ihn dann so:
# .github/workflows/ci.yml
jobs:
test:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '20'Composite Actions sind noch granularer — perfekt für wiederverwendbare Step-Gruppen, die du über Repos hinweg teilen willst.
🏗️ Praxisbeispiel: Die komplette Pipeline
Jetzt setzen wir alles zusammen. Eine echte Pipeline: Lint, Test, Build, Deploy — mit Abhängigkeiten zwischen den Jobs:
name: Full Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
build:
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
deploy:
needs: build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- name: Deploy
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
echo "Deploying build artifacts..."
# Dein Deployment-Befehl hierBeachte die needs-Eigenschaft: Build wartet auf Lint und Test. Deploy wartet auf Build. Und Deploy läuft nur auf main bei einem Push — nicht bei Pull Requests. Sauber.
🏠 Self-Hosted Runner fürs Homelab
Die GitHub-Runner sind super für die meisten Projekte. Aber manchmal brauchst du mehr Kontrolle: spezielle Hardware, Zugriff aufs lokale Netzwerk oder einfach unbegrenzte Build-Minuten.
# Runner auf deinem Server einrichten
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64-2.311.0.tar.gz
tar xzf ./actions-runner-linux-x64.tar.gz
./config.sh --url https://github.com/DEIN-USER/DEIN-REPO --token DEIN-TOKEN
./run.shIm Workflow wechselst du einfach den Runner:
jobs:
build:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- run: echo "Läuft auf meinem eigenen Server!"Für Homelab-Setups ist das Gold wert. Du kannst direkt auf lokale Dienste zugreifen, Docker-Images lokal bauen und alles in deinem Netzwerk deployen.
💰 Pricing: Was kostet der Spaß?
| Plan | Minuten/Monat | Speicher | Preis |
|---|---|---|---|
| Free | 2.000 | 500 MB | $0 |
| Team | 3.000 | 2 GB | $4/User/Monat |
| Enterprise | 50.000 | 50 GB | $21/User/Monat |
Wichtig: Die Minuten gelten nur für private Repos. Öffentliche Repos haben unlimitierte Minuten. Und Self-Hosted Runner verbrauchen keine Minuten — ein weiterer Grund fürs Homelab.
macOS-Runner kosten 10x so viel wie Linux-Runner. Windows-Runner das Doppelte. Wenn du sparen willst: Linux nehmen und macOS-Builds nur wenn wirklich nötig.
💡 Fazit
GitHub Actions ist ein mächtiges Werkzeug, das direkt in deinem Repository lebt. Kein externer Service, kein kompliziertes Setup. Du schreibst eine YAML-Datei und bekommst dafür automatisierte Tests, Builds und Deployments.
Fang klein an: Ein einfacher CI-Workflow mit Lint und Tests. Dann bau Schritt für Schritt aus — Caching, Matrix-Builds, Docker, Deployment. Und wenn du mehr Kontrolle brauchst, stell dir einen Self-Hosted Runner ins Homelab.
Die 2.000 kostenlosen Minuten pro Monat reichen für die meisten persönlichen Projekte locker aus. Und für Open-Source-Projekte? Komplett kostenlos. Keine Ausrede mehr, CI/CD nicht einzusetzen.
Happy automating! 🚀
Mehr Artikel entdecken
OAuth 2.0 & OpenID Connect: Finally Understanding Authentication 🔐
TL;DR: OAuth 2.0 handles authorization (who can do what), OpenID Connect adds authentication (who are you) on top. Together they form the backbone of modern login systems like "Sign in with Google". This article breaks down how it all fits together, which grant types exist, what
GitHub Actions: CI/CD for Your Projects 🔄
TL;DR: GitHub Actions is GitHub's built-in CI/CD system. You define workflows as YAML files that run on push, pull request, or on a schedule. In this article, I'll walk you through everything: from your first workflow to matrix builds, Docker images, self-hosted runners, and
Spoolman: Deine Filament-Rollenverwaltung
Du verlierst den Überblick über deine Filamente? Ich habe eine Lösung dafür! 🛵
GitHub Repos automatisiert archivieren
GitHub-Repos schnell und einfach archivieren? Ich zeig dir wie!