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.

GitHub Actions Documentation
Automate, customize, and execute your software development workflows right in your repository with GitHub Actions.

🧩 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.
GitHub Marketplace - Actions
Tausende vorgefertigte Actions für jeden Anwendungsfall.

🚀 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 test

Das 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.

Git: Geschichte, Anwendung und Vorteile
Alles über Git — die Grundlage für GitHub Actions.

🔢 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 test

GitHub 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 ci

Die 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/deploy

Wichtig: 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.

Docker: Einfache Bereitstellung von Diensten
Alles über Docker — perfekt in Kombination mit GitHub Actions.

🌐 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 myapp

Egal 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 test

Aufrufen 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 hier

Beachte 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.

Semantic Versioning: Ein Leitfaden für Entwickler
Versionierung und CI/CD gehören zusammen — hier erfährst du wie.

🏠 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.sh

Im 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! 🚀

Artikel teilen:Share article: