TL;DR — Kurzzusammenfassung
GitHub Actions Deployment-Guide: automatisierte Pipelines mit Umgebungsschutz, Secrets, mehreren Deploy-Zielen, Caching und Rollback-Strategien erstellen.
Manuelles Deployen — per SSH auf einen Server verbinden, Build-Befehle ausführen, die Daumen drücken — ist fehleranfällig, nicht nachvollziehbar und skaliert nicht. GitHub Actions ermöglicht es, die gesamte Deployment-Pipeline als Code zu definieren: Lint, Tests, Build, Genehmigung, Deployment und Benachrichtigung — alles automatisch ausgelöst bei jedem Push auf main. Dieser Leitfaden deckt die Erstellung eines produktionstauglichen automatisierten Deployment-Workflows ab, vom ersten YAML-File bis zu Multi-Target-Deployments, Umgebungsschutz und Rollback-Strategien.
Voraussetzungen
Bevor Sie Ihren Deployment-Workflow erstellen, stellen Sie sicher, dass Folgendes vorhanden ist:
- Ein GitHub-Repository mit Anwendungscode (Node.js-Beispiele im gesamten Leitfaden)
- Eine Zielumgebung — Cloudflare-Workers-Konto, ein VPS mit SSH-Zugang oder ein selbst-gehosteter Windows-Runner für IIS
- YAML-Grundkenntnisse — GitHub-Actions-Workflows sind YAML-Dateien, bei denen Einrückungen wichtig sind
- Zugriff auf die Repository-Einstellungen in GitHub zum Speichern von Secrets und Erstellen von Umgebungen
GitHub Actions Grundlagen
Ein GitHub-Actions-Workflow ist eine YAML-Datei, die in .github/workflows/ gespeichert ist. Er wird durch ein oder mehrere Ereignisse ausgelöst (Push, Pull Request, Zeitplan, manueller Auslöser). Ein Workflow enthält einen oder mehrere Jobs. Jeder Job läuft auf einem Runner — einer virtuellen Maschine, die von GitHub gehostet wird (Ubuntu, Windows, macOS) oder selbst gehostet ist. Jeder Job enthält sequentielle Steps, von denen jeder entweder einen Shell-Befehl (run) oder eine wiederverwendbare Einheit namens Action (uses) ausführt.
Die grundlegenden Bausteine:
name: Deploy # Workflow-Name im Actions-Tab
on: # Auslöser
push:
branches: [main]
jobs:
deploy: # Job-ID
runs-on: ubuntu-latest # Runner
steps:
- uses: actions/checkout@v4 # Action (lädt Quellcode herunter)
- run: npm ci # Shell-Befehl
- run: npm run build
Jobs laufen standardmäßig parallel. Verwenden Sie needs: [job-id], um eine Reihenfolge zu erzwingen.
Ihr Erster Deployment-Workflow
Hier ist ein vollständiger Workflow für ein Node.js-Projekt, das auf Cloudflare Workers mit Wrangler deployt. Er führt zunächst Lint und Tests durch, dann den Build und zuletzt das Deployment — nur bei Pushes auf main.
name: Auf Cloudflare Workers deployen
on:
push:
branches: [main]
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- run: npm ci
- run: npm run lint
- run: npm test
build:
needs: lint-and-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Mit Wrangler deployen
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- name: Erfolg in Slack melden
if: success()
run: |
curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-H "Content-Type: application/json" \
-d "{\"text\":\"${{ github.sha }} von ${{ github.actor }} in Produktion deployt\"}"
Der concurrency-Block oben verhindert, dass zwei Deployments sich überlappen. Wenn Sie zweimal schnell hintereinander pushen, wird die erste Ausführung abgebrochen, bevor die zweite beginnt.
Umgebungsschutzregeln
Direkt in die Produktion zu pushen ohne menschliche Überprüfung ist für kritische Systeme riskant. GitHub-Umgebungen ermöglichen es, Schutzregeln zu jedem Job hinzuzufügen, der sie anvisiert.
So konfigurieren Sie den Umgebungsschutz:
- Gehen Sie zu Settings → Environments in Ihrem Repository
- Klicken Sie auf New environment und nennen Sie es
production - Aktivieren Sie Required reviewers — fügen Sie einen oder mehrere GitHub-Benutzer oder -Teams hinzu
- Legen Sie optional einen Wait timer fest (z. B. 10 Minuten), bevor der Job ausgeführt werden darf
- Beschränken Sie optional, welche Branches in diese Umgebung deployen können (z. B. nur
main)
Einmal konfiguriert, pausiert jeder Job mit environment: production und wartet auf die Genehmigung eines Reviewers, bevor er fortfährt. Der Reviewer sieht das ausstehende Deployment in der GitHub-Oberfläche und kann es mit einem Kommentar genehmigen oder ablehnen.
Verwaltung von Secrets
Codieren Sie niemals API-Tokens, SSH-Schlüssel oder Passwörter fest in Workflow-Dateien. GitHub bietet drei Ebenen der Secret-Speicherung:
Repository-Secrets — Für alle Workflows im Repo verfügbar. Gehen Sie zu Settings → Secrets and variables → Actions → New repository secret.
Umgebungs-Secrets — Auf eine bestimmte Umgebung (z. B. production) beschränkt. Werden nur exponiert, wenn ein Job auf diese Umgebung abzielt.
Organisations-Secrets — Zwischen mehreren Repositories einer GitHub-Organisation geteilt, ideal für Tokens, die von vielen Projekten genutzt werden.
Referenzieren Sie Secrets in Workflows mit der Syntax ${{ secrets.SECRET_NAME }}:
- name: Mit Wrangler deployen
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
OIDC (OpenID Connect) ist eine Alternative zu langlebigen Secrets für Cloud-Anbieter, die es unterstützen (AWS, GCP, Azure). Anstatt ein statisches Token zu speichern, fordert der Runner zur Laufzeit kurzlebige Credentials vom Cloud-Anbieter an.
Deployment auf Verschiedene Ziele
Cloudflare Workers mit Wrangler
Die Action cloudflare/wrangler-action übernimmt die Authentifizierung und führt jeden Wrangler-Befehl aus:
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env production
VPS über SSH und rsync
Für einen Linux-VPS verwenden Sie SSH zur Verbindung und rsync zur Dateiübertragung:
- name: Auf VPS deployen
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
script: |
rsync -az --delete dist/ /var/www/myapp/dist/
systemctl restart myapp
Speichern Sie den privaten SSH-Schlüssel in GitHub Secrets. Fügen Sie den entsprechenden öffentlichen Schlüssel zu ~/.ssh/authorized_keys auf dem Server hinzu. Verwenden Sie einen dedizierten Deploy-Benutzer mit minimalen Berechtigungen — niemals root.
IIS über Selbst-Gehosteten Runner
Für Windows-IIS-Deployments führen Sie einen selbst-gehosteten Runner direkt auf dem Server aus:
deploy-iis:
needs: build
runs-on: [self-hosted, iis-deploy]
environment: production
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: C:\inetpub\wwwroot\myapp
- name: IIS-Site neu starten
run: |
Import-Module WebAdministration
Restart-WebItem 'IIS:\Sites\MyApp'
shell: powershell
Caching und Performance
Dependency-Caching
Das erneute Herunterladen von npm-Paketen bei jeder Ausführung verschwendet Zeit. Cachen Sie sie:
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
Für .NET:
- uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: ${{ runner.os }}-nuget-
Concurrency-Gruppen
Concurrency-Gruppen verhindern parallele Deployments — eine häufige Ursache für Race Conditions:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Vergleich: GitHub Actions vs. GitLab CI vs. Jenkins
| Merkmal | GitHub Actions | GitLab CI | Jenkins |
|---|---|---|---|
| Hosting | GitHub (SaaS) | GitLab oder selbst-gehostet | Nur selbst-gehostet |
| Konfigurationsdatei | .github/workflows/*.yml | .gitlab-ci.yml | Jenkinsfile (Groovy) |
| Kostenloser Plan | 2.000 Min./Monat (privat) | 400 Min./Monat (privat) | Unbegrenzt (eigene Infra) |
| Marketplace | 20.000+ Actions | Begrenzt | 1.800+ Plugins |
| Umgebungs-Gates | Nativ (kostenlos für öffentlich) | Nativ (Ultimate für Regeln) | Über Plugins |
| OIDC | AWS, GCP, Azure, Cloudflare | AWS, GCP, Azure | Über Plugins |
| Lernkurve | Niedrig | Niedrig | Hoch (Groovy-DSL) |
| Ideal für | Projekte auf GitHub | Monorepos auf GitLab | Komplexe Legacy-Pipelines |
Praxisszenario: Vollständige Pipeline
Eine Produktions-Pipeline für Node.js mit Slack-Benachrichtigungen:
name: Produktions-Deployment
on:
push:
branches: [main]
concurrency:
group: production-deploy
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- run: npm ci
- run: npm run lint
test:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- run: npm ci
- run: npm test -- --coverage
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist-${{ github.sha }}
path: dist/
retention-days: 14
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: dist-${{ github.sha }}
path: dist/
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- name: Erfolg melden
if: success()
run: |
curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-d "{\"text\":\"${{ github.sha }} von ${{ github.actor }} deployt\"}"
- name: Fehler melden
if: failure()
run: |
curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-d "{\"text\":\"FEHLER beim Deployment von ${{ github.sha }}\"}"
Status-Badges
Fügen Sie ein Live-Status-Badge zu Ihrer README hinzu:

Fallstricke und Grenzfälle
Secret-Maskierung funktioniert nur bei exakten Übereinstimmungen. GitHub maskiert den literalen Secret-Wert in den Logs, aber wenn das Secret vor der Verwendung base64- oder URL-kodiert wird, kann der Originalwert unmaskiert erscheinen.
Runner-Image-Änderungen brechen angeheftete Tool-Versionen. GitHub aktualisiert ubuntu-latest regelmäßig. Wenn Ihr Workflow auf einer bestimmten vorinstallierten Tool-Version basiert, installieren Sie sie explizit mit einer Setup-Action.
cancel-in-progress: true bricht die aktuelle Ausführung ab, nicht die eingehende. Der neue Push gewinnt. Das ist für Deployments fast immer das Gewünschte.
Umgebungs-Secrets stehen bei Fork-Pull-Requests nicht zur Verfügung. Das ist absichtlich — Forks können keine Secrets zugreifen, um Datenlecks zu verhindern.
Fehlerbehebung
Workflow wird beim Push auf main nicht ausgelöst. Überprüfen Sie den Filter on.push.branches — der Branch-Name muss exakt übereinstimmen. Bestätigen Sie, dass die Workflow-Datei in .github/workflows/ liegt.
Secret ist verfügbar, aber die Wrangler-Authentifizierung schlägt fehl. Stellen Sie sicher, dass der Secret-Name exakt übereinstimmt (Groß-/Kleinschreibung beachten). Verifizieren Sie, dass das Cloudflare-API-Token die richtigen Berechtigungen hat: Workers Scripts: Edit und Account: Read.
Job wartet unbegrenzt auf Umgebungsgenehmigung. Überprüfen Sie, dass Sie als erforderlicher Reviewer der Umgebung aufgeführt sind. Bestätigen Sie, dass der Umgebungsname im Workflow-YAML exakt mit dem Namen in den Settings übereinstimmt.
Zusammenfassung
- Definieren Sie Workflows als YAML-Dateien in
.github/workflows/— sie sind gemeinsam mit Ihrem Code versioniert - Strukturieren Sie Pipelines als sequentielle Jobs: lint → test → build → deploy, mit
needszur Durchsetzung der Reihenfolge - Verwenden Sie
environment: productionmit erforderlichen Reviewern für menschliche Genehmigungstore - Speichern Sie alle Secrets im GitHub-Secrets-Store — niemals in der YAML-Datei selbst
- Cloudflare-Workers-Deployments verwenden
cloudflare/wrangler-action; VPS-Ziele nutzen SSH/rsync; IIS verwendet einen selbst-gehosteten Runner - Fügen Sie
concurrency-Gruppen mitcancel-in-progress: truehinzu, um überlappende Deployments zu verhindern - Cachen Sie Abhängigkeiten mit
actions/setup-node cache: npmfür schnellere Ausführungen - Benennen Sie Artefakte mit
${{ github.sha }}für einfaches Rollback durch erneutes Ausführen des Workflows auf einem früheren Commit