Themen per FTP hochzuziehen endet 2026 meist gleich: eine Datei aus inc/ fehlt, composer.lock stammt von einem anderen Laptop, und Produktion liegt, weil jemand wp-config.php überschrieben hat. Continuous Integration und Continuous Deployment sind keine Enterprise-Show - sie sorgen dafür, dass jede Git-Änderung denselben Build, dieselben Tests und denselben Weg auf den Server nimmt.
Dieser Leitfaden gilt für WordPress mit Theme oder Plugin im Repository (Composer, npm, optional Docker), nicht für Seiten, die nur im Admin leben. Liegt Custom-Code noch nur in Datenbank und Medienbibliothek, bring Themes und eigenen Code zuerst in Git - sonst hat CI/CD nichts zu bauen.
Runner-Dokumentation: GitHub Actions. Parallel lesenswert: Hardening WordPress. Für Befehle nach dem Deploy: WP-CLI.
Warum FTP und „kurz hochladen“ Audits nicht überstehen
Drei Muster tauchen in Audits deutscher WooCommerce-Shops und Agentur-Übergaben immer wieder auf:
- Umgebungsdrift - lokal PHP 8.2 und Composer 2.7, auf dem Hetzner-VPS PHP 8.1 ohne
intl. Lokal „läuft“composer install, Produktion stirbt an class not found. - Kein Audit-Trail - nach einem Vorfall ist unklar, welcher Commit live war. Das Hosting-Panel zeigt ein Dateidatum, keine Git-SHA.
- Secrets auf der Platte - SFTP-Passwörter in FileZilla oder eine „kurz“ committed
.envbleiben der klassische Leak-Pfad - und bei DSGVO-Fällen teuer.
CI/CD löst das mechanisch: Build auf bekanntem Image (shivammathur/setup-php oder eigenes Docker), Artefakt versioniert nach SHA, Deploy per SSH-Key aus Secrets.
Minimaler Pipeline-Pfad: Push, Build, Test, Deploy
Typischer Workflow für Custom-Theme plus Composer-Plugins:
- Trigger - Push auf
mainoder Merge eines reviewed Pull Requests.developgeht oft nur auf Staging. - Build -
composer install --no-dev --optimize-autoloader,npm ci,npm run build. PHP und Node im Workflow pinnen (php-version: '8.2',node-version: '20'). - Test - PHPUnit für Plugins, optional Playwright auf dem Checkout. Fail = Stop, kein Deploy.
- Artefakt - Release ohne
.git, ohnenode_modules, mitvendor/und gebautemassets/dist/. - Deploy - rsync/SCP nach
releases/<run_id>/, danach atomares Symlink-Umschalten.
Kurzskizze in GitHub Actions:
name: deploy-production
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- run: composer install --no-dev --optimize-autoloader
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- run: npm ci && npm run build
- name: rsync release
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
# Key schreiben, rsync nach releases/$GITHUB_SHA, ln -sfnDas volle Symlink-Skript liegt im Repo als bin/release.sh - der Workflow ruft es nur auf. Derselbe Skriptpfad funktioniert vom Laptop, wenn Actions ausfällt.
Atomare Deploys statt Dateien überschreiben
Dateien „in place“ unter public_html zu überschreiben heißt: ein paar Sekunden lang sieht die Hälfte der Requests altes functions.php und neues Template. Bei WooCommerce in der Black-Week-Woche kann das Warenkorb-Sessions zerstören.
Atomares Layout:
- Basis:
/var/www/site/ - Releases:
/var/www/site/releases/20260920-142211/ - Shared: Uploads,
wp-config.php, Object-Cache - außerhalb des Releases current→ Symlink auf den aktiven Release
Nach erfolgreichem rsync:
ln -sfn /var/www/site/releases/20260920-142211 /var/www/site/currentNginx root zeigt auf .../current/public (oder das Äquivalent). Der Wechsel ist sofort. Alte Releases nach N Tagen löschen oder die letzten drei für Rollback behalten.
WordPress-Uploads müssen außerhalb des Releases liegen - sonst löscht jeder Deploy Medien oder kopiert Gigabytes. Klassiker: shared/uploads verlinkt nach wp-content/uploads in jedem Release.
Staging, Previews und Strategie-Matrix
2026 ist Feature-Branch → PR → Staging → main/Produktion für Shops und Lead-Seiten nicht optional. Staging braucht:
- dieselbe PHP-Version und dieselben Extensions wie Produktion
- eine DB-Kopie (anonymisiert, wenn personenbezogene Daten unter der DSGVO liegen)
- Payment-Secrets nur im Sandbox-Modus
Preview-URLs pro PR helfen bei Redesign und CSS, weniger bei Schema-Migrationen - die testest du auf Staging mit einem echten Dump.
| Strategie | Risiko | Pflegeaufwand | Wann sinnvoll |
|---|---|---|---|
| Manuelles FTP | Sehr hoch | Günstig starten, teure Ausfälle | Nur Sandbox / Lernen |
| Git-Hook auf dem Server | Mittel | Niedrig | Kleines Portfolio, ein Entwickler |
| CI + atomares rsync | Niedrig | Mittel | Business, WooCommerce, Agenturen |
| Blue/Green oder zwei VPS | Minimal | Hoch | Viel Traffic, SLA, Zahlungen |
Blue/Green (zwei volle Umgebungen, Load-Balancer-Umschaltung) lohnt sich selten unter hunderten parallelen Sessions. Für die meisten DACH-Shops reicht atomarer Symlink plus Verzeichnis-Rollback - oft auf Hetzner oder einem Managed-Host mit SSH.
Secrets, WP-CLI und Datenbank
Nie committen:
- SSH-Keys
- Datenbankpasswörter
AUTH_KEY/SECURE_AUTH_KEYauswp-config.php- API-Tokens von Payment-Gateways (PayPal, Stripe, deutsche PSP)
In GitHub: Settings → Secrets and variables → Actions. Als env im Deploy-Schritt injizieren. Auf dem Server liegt wp-config.php unter shared/ und ist kein Teil des CI-Artefakts.
WP-CLI nach dem Symlink-Wechsel:
wp cache flushbei Object Cachewp rewrite flushnach CPT-Änderungenwp plugin listals Smoke-Zeile im Log
Datenbank-Migrationen getrennt vom Datei-Deploy planen. Symlink-Rollback macht kein ALTER TABLE rückgängig. Bei destruktiven Migrationen: zuerst Backup (wp db export auf Staging und Produktion), dann Migration, dann Traffic auf Code, der das Schema braucht - oder Feature-Flag in PHP.
PHP-Abhängigkeiten: Composer install - in CI für Produktion immer --no-dev und gesperrtes composer.lock.
Was den ersten WordPress-Pipeline-Lauf kaputt macht
Aus Praxis auf VPS (Hetzner, DigitalOcean, OVH) und Managed mit SSH:
.gitignorefrisstvendor/- Produktion bekommt ein Theme ohne Autoloader. Entweder Vendor auf dem Runner bauen und ins Artefakt packen, oder Composer nach dem rsync auf dem Server laufen lassen (dann braucht der Server Composer und Packagist-Zugang).- Falsche Pfade - Skript erwartet
/var/www/html, Hosting nutzt/home/user/domains/.... Pfade in Secrets pro Umgebung pinnen. - Opcache hält alten Code - nach
ln -sfnPHP-FPM reloaden oderopcache_resetper WP-CLI/mu-Plugin nur für den Deploy-User. - Cron und Queues - Action Scheduler von WooCommerce kann Jobs noch Minuten auf altem Code fahren; nach großen Releases Failed Jobs kurz beobachten.
Smoke-Test nach dem Deploy (curl vom Runner oder eigener Job):
- Startseite HTTP 200
/wp-login.php200- eine kritische Produkt- oder Formular-URL
- optional HEAD an das CDN bei gehashten Assets
Schlägt Smoke fehl, ist automatisches Symlink-Rollback billiger als der Kundenanruf um 23:00.
Block-Themes, Must-Use-Plugins und Git-Ausnahmen
Nicht jedes WordPress-Stück passt in dieselbe Pipeline. Inhalte in der Datenbank (Beiträge, ACF-Field-Groups ohne JSON-Export, Elementor-CSS) leben außerhalb des Releases. CI/CD schützt Code, ersetzt kein DB-Backup.
Praktische Trennung:
- Klassisches oder hybrides Theme mit Vite - voller CI-Build, Artefakt mit
style.css+dist/. - Block-Theme (FSE) -
theme.jsonund HTML-Templates in Git; Änderungen aus dem Site-Editor zurück per PR, sonst divergieren Produktion und Git nach dem ersten „Speichern“. - Must-Use-Plugins - mit dem Release ausrollen; nicht aus dem Admin updaten. Guter Ort für Deploy-Healthchecks und das Abschalten des Dateieditors.
- Plugins von wordpress.org - Versionen per Composer (
wpackagist) pinnen oder in einem reviewed Job aktualisieren. „Update im Admin“ gemischt mit „CI überschreibtplugins/“ endet in einem Zufallsbaum.
Agentur-Stacks im DACH-Raum mischen oft Custom-Theme, WooCommerce aus wpackagist und zwei Premium-ZIPs über ein Composer-Path-Repository. Die Pipeline muss alle drei Quellen kennen, sonst unterscheiden sich Staging und Produktion um ein ZIP von vor einem halben Jahr.
Checkliste vor dem ersten Actions-Deploy
Bevor main an Produktion hängt, diese Liste einmal durchgehen - spart ein Wochenende:
- Privates Repo oder Historie ohne Secrets (
git log -pzeigt keine alten.env). - Branch Protection: Pflicht-PR, grüner
test-Workflow. - Getrennte Secrets:
DEPLOY_HOST_STAGING,DEPLOY_HOST_PROD, eigene SSH-Keys. - Staging mit derselben PHP-Version wie Produktion (nicht „wir 8.3, Kunde 8.1“).
- DB-Backup vor dem ersten atomaren Umschalten - auch wenn Code-Rollback steht.
- Uptime-Monitoring (UptimeRobot, Better Stack o. Ä.) mit SMS/Slack auf die Produktions-URL.
- Einseitiges Runbook: manuelles
ln -sfnauf den vorherigen Release, wer Serverzugriff hat.
Erst dann Auto-Deploy von main. Bis dahin reichen „Build + Test“ auf PRs und manueller workflow_dispatch auf Staging.
Zusammenfassung
CI/CD für WordPress ist kein „magisches YAML“, sondern ein wiederholbarer Vertrag: dasselbe PHP, derselbe Lockfile, dasselbe Artefakt, Umschalten ohne Fenster halb hochgeladener Dateien. Du startest mit Git und Staging, legst Actions für Composer/npm drauf und endest bei atomarem current plus Smoke-Test. FTP wird Notfallwerkzeug, nicht Prozess.
Wenn du für Shop oder Lead-Seite einen WordPress-Entwickler brauchst, der die Pipeline neben dem Theme-Code hält, arbeitet WPPoland am realen Stack - nicht an Folien.







