Was ist ein Webhook und warum自动化?
Ein Webhook ist ein HTTP-Callback — ein POST-Request, den ein externer Service an deinen Server sendet, wenn ein bestimmtes Ereignis eintritt. GitHub sendet einen Webhook nach jedem Push, Stripe nach jeder Zahlung, Travis CI nach jedem Build. Dein Server empfängt den Request und reagiert darauf.
Ohne Webhooks: dupushst Code, loggst dich per SSH ein, ziehst den neuen Stand, startest den Server neu. Mit Webhooks: du pushst Code, GitHub benachrichtigt deinen Server, dein Server pullt, baut, deployed — vollautomatisch, in Sekunden.
Das Ziel ist nicht nur Bequemlichkeit — es ist Consistenz und Schnelligkeit. Manuell deployen heisst: unterschiedliche Personen machen es unterschiedlich, zu unterschiedlichen Zeiten, mit unterschiedlicher Sorgfalt. Automatisiert deployen heisst: der gleiche流程 immer, präzise dokumentiert, auditable.
Webhook ≠ Cron-Job
Ein Cron-Job läuft zeitbasiert (z.B. "jeden Tag um 3 Uhr"). Ein Webhook läuft ereignisbasiert ("wenn etwas passiert"). Webhooks sind das richtige Werkzeug für: Push-to-Deploy, Payment-Benachrichtigungen, externe Monitoring-Alerts, Third-Party-Integrationen. Cron-Jobs sind richtig für: regelmässige Backups, SEO-Sitemap-Generierung, Reporting.
Eigenen Webhook-Endpoint bauen
Der einfachste Weg: ein Express.js-Endpoint, der GitHub-Push-Events verarbeitet. Der Endpoint muss:
- POST-Requests akzeptieren (nur POST, nie GET).
- Den Payload verifizieren (HMAC-SHA256 Signature, damit niemand Fake-Events injizieren kann).
- Einen Prozess im Hintergrund starten (das Deployment), damit der Endpoint schnell antwortet.
- Logs schreiben, damit du Fehler debuggen kannst.
// routes/webhook.js — Webhook-Endpoint für GitHub/arbitrary webhooks
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const { exec } = require('child_process');
const fs = require('fs');
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
const DEPLOY_SCRIPT = '/opt/deploy.sh';
const LOG_FILE = '/var/log/webhook-deploy.log';
function log(msg) {
const line = `[${new Date().toISOString()}] ${msg}\n`;
fs.appendFileSync(LOG_FILE, line);
console.log(line.trim());
}
function verifyGitHubSignature(payload, signature) {
if (!WEBHOOK_SECRET) return true; // Dev mode
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
const digest = 'sha256=' + hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
router.post('/deploy', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-hub-signature-256'] || req.headers['x-gitlab-token'];
const event = req.headers['x-github-event'] || req.headers['x-gitlab-event'];
// 1. Verify signature
if (signature && !verifyGitHubSignature(req.body, signature)) {
log(`REJECT: Invalid signature from ${req.ip}`);
return res.status(403).json({ error: 'Invalid signature' });
}
// 2. Nur Push-Events automatisch deployen
if (event === 'push') {
const payload = JSON.parse(req.body);
const branch = payload.ref?.split('/').pop();
if (branch !== 'main' && branch !== 'master') {
log(`SKIP: Push to ${branch} — not main/master`);
return res.json({ status: 'skipped', reason: `Branch ${branch} not deployed` });
}
log(`DEPLOY: Push to ${branch} by ${payload.pusher?.name}`);
// 3. Async deployment starten (nicht im Request-Thread blockieren)
const deployCmd = `bash ${DEPLOY_SCRIPT} 2>&1 >> ${LOG_FILE} &`;
exec(deployCmd, (err) => {
if (err) log(`ERROR starting deploy: ${err.message}`);
});
// 4. Sofort antworten, damit GitHub keinen Timeout bekommt
return res.json({ status: 'deploying', message: 'Deployment gestartet' });
}
// Andere Events: nur loggen, nicht deployen
log(`EVENT: ${event} received (no action)`);
return res.json({ status: 'received', event });
});
module.exports = router;
Das Deployment NIEMALS im Request-Thread starten
Dein Webhook-Endpoint muss IMMER sofort antworten (innerhalb von ~5 Sekunden). GitHub und andere Services haben Timeouts — wenn der Endpoint länger braucht, interpretiert der Service das als Fehler und wiederholt den Webhook (bis zu 72 Stunden lang, Exponential Backoff). Das Deployment im Hintergrund-Process starten (& am Ende des exec-Aufrufs), damit der Endpoint sofort { status: 'deploying' } zurückgibt.
Das Deployment-Script (deploy.sh)
Das Script, das der Webhook-Endpoint aufruft, ist der Kern des automatisierten Deployments:
#!/bin/bash
# /opt/deploy.sh — Automated deployment script
set -e
LOG="/var/log/deploy-$(date +%Y%m%d-%H%M%S).log"
exec > >(tee -a "$LOG") 2>&1
echo "=== Deployment gestartet: $(date) ==="
cd /opt/app
# 1. Git stash (Safety — sollte nie nötig sein wenn nur main gemergt wird)
git stash || true
# 2. Pull latest
echo "Pulling latest main..."
git pull origin main
# 3. Dependencies
echo "Installing dependencies..."
npm ci --production
# 4. Database migrations
echo "Running migrations..."
node migrate.js
# 5. Health check vor dem Switch
echo "Building..."
npm run build 2>/dev/null || true
# 6. PM2 Graceful Reload (Zero-Downtime)
echo "Reloading PM2 process..."
pm2 reload app || pm2 restart app
# 7. Final health check
sleep 3
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health)
if [ "$HTTP_STATUS" = "200" ]; then
echo "=== Deployment erfolgreich (HTTP $HTTP_STATUS) ==="
else
echo "=== FEHLER: Health-Check fehlgeschlagen (HTTP $HTTP_STATUS) ==="
echo "Rollback wird eingeleitet..."
git reset --hard HEAD~1
pm2 reload app
exit 1
fi
GitHub Actions: Alternativer Weg ohne Server-Webhook
GitHub Actions ist eine elegante Alternative zum eigenen Webhook-Server. Du brauchst keinen eigenen Endpoint — GitHub Actions läuft in der GitHub-Cloud und deployt direkt auf deinen Server per SSH:
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy to Server
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
port: 22
script: |
cd /opt/app
git pull origin main
npm ci --production
node migrate.js
pm2 reload app
curl -f http://localhost:3000/health || exit 1
Die secrets.SERVER_* werden in GitHub Settings → Secrets and Variables → Actions definiert. Das ist deutlich sicherer als Credentials in der Repository zu speichern — und du brauchst keinen eigenen Webhook-Server.
| Approach | Vorteile | Nachteile | Eignung |
|---|---|---|---|
| Eigener Webhook-Endpoint | Vollständige Kontrolle, keine Drittanbieter-Abhängigkeit, flexibel | Eigenes Security-Monitoring nötig, Endpoint muss öffentlich erreichbar sein | Geheime/sensitive Setups, Self-Hosted GitLab, Custom-Trigger |
| GitHub Actions | Kein eigener Endpoint, Secrets-Management, gute UI, kostenlos für Public Repos | GitHub-Abhängigkeit, komplexere Secrets-Config, nicht alle Anbieter unterstützt | Open-Source-Projekte, GitHub-Nutzer, schnelle Setups |
| Render / Vercel Auto-Deploy | Null Konfiguration, eingebautes CI/CD, Preview-Deployments | Vendor Lock-in, weniger Kontrolle, Platinslimitiert | Small Teams, Startups ohne DevOps-Kapazität |
Secrets sicher verwalten
Credentials (API-Keys, Datenbank-URLs, Webhook-Secrets) gehorchen denselben Regeln wie Passwörter — und müssen entsprechend geschützt werden. Hier die Mindestanforderungen:
- Niemals Secrets in Git — .env-Dateien in
.gitignore, Secrets in ENV-Variablen auf dem Server. - Webhook-Signatur verifizieren — HMAC-SHA256, damit kein Unbefugter Fake-Events senden kann.
- Keine Credentials in Logs — im Log-Script sicherstellen, dass Payload-Substrings nicht ins Log geschrieben werden.
- Secrets rotieren — alle 90 Tage oder sofort, wenn ein Leak vermutet wird.
Tools für Secrets-Management: dotenv für lokale Entwicklung, Render/Vercel Environment Variables für produktive Secrets, HashiCorp Vault für komplexe Setups, agenclave für sensitive secrets auf dem Server.
Rollback: Wenn das Deployment schief geht
Ein automatisierter Deploy ohne Rollback-Strategie ist ein Risiko. Wenn ein Deployment fehlschlägt, willst du in Sekunden auf die vorherige Version zurückwechseln können — nicht in 10 Minuten manueller Arbeit.
# /opt/rollback.sh — Schneller Rollback auf vorherigen Commit
#!/bin/bash
set -e
echo "=== Rollback gestartet: $(date) ==="
cd /opt/app
# Letzten funktionierenden Commit aus Log extrahieren
LAST_GOOD=$(git rev-parse HEAD~1)
echo "Rolling back to: $LAST_GOOD"
git reset --hard $LAST_GOOD
npm ci --production
node migrate.js
pm2 reload app
sleep 3
curl -f http://localhost:3000/health && echo "Rollback erfolgreich" || echo "Rollback FEHLGESCHLAGEN"
Deployment erst nach Health-Check als "erfolgreich" markieren
Dein deploy.sh MUSS einen finalen Health-Check machen (GET auf /health) und bei Nicht-200 automatisch rollen. Das verhindert, dass ein fehlerhaftes Deployment als "live" markiert wird. Bei Render/Vercel passiert das automatisch — bei eigenem VPS musst du es selbst einbauen.
Monitoring: Webhook-Deployments überwachen
Ein automatisiertes Deployment ist nur so gut wie sein Monitoring. Ohne Logs und Alerts weisst du nicht, ob deploys erfolgreich sind — bis ein User es dir meldet.
| Monitor | Was es checkt | Setup |
|---|---|---|
| Deployment-Log | stdout/stderr jedes Deploys | /var/log/webhook-deploy.log — täglich rotieren |
| PM2 Logs | App-Ausgabe inkl. Errors | pm2 logs --lines 100 |
| Uptime Monitoring | Health-Endpoint nach Deployment | UptimeRobot / BetterStack (kostenlos für 1 Check/min) |
| Disk Space | Log-Rotation verhindern | df -h im Cron, Alert wenn <10% frei |
| GitHub Actions Run | Erfolg/Fehler der CI-Pipeline | GitHub Notifications / Slack-Integration |
Fazit: Automatisiere, aber nicht blind
Webhook-basierte Deployments eliminieren manuellen Aufwand und Konsistenz-Probleme — aber nur, wenn du Security und Monitoring von Anfang an einbaust. Das Minimum: HMAC-Signatur-Verifikation, Health-Check nach jedem Deployment, automatisierter Rollback.
Für die meisten Projekte: GitHub Actions mit SSH-Deploy auf einen VPS. Es braucht fast keine Konfiguration, Secrets-Management ist integriert, und du hast volle Sichtbarkeit über den Deploy-Status. Nur wenn du eine spezielle Trigger-Logik brauchst (z.B. Deployment nur nach Manual-Review) oder GitHub nicht nutzt, lohnt sich ein eigener Webhook-Endpoint.
VPS für automatisiertes Deployment
Root-Zugang, Git-Deployment, SSH-Zugang — alle Voraussetzungen für automatische Webhook-basierte Deployments.
Zum Hosting-Vergleich