PHP-Entwickler in Karlsruhe

PHP-Entwickler für Karlsruher Unternehmen: Ich löse FTP-Uploads durch ein wiederholbares Deployment ab, mit Docker-Image, Composer, Migrationen, Release-Ordnern und Rollback. Einzelentwickler, remote, schriftlich.

PHP-Entwickler

Mein Platz in der WordPress-Community

  • Mein wordpress.org-Konto besteht seit 2006.
  • Ich habe zum WordPress-Core in den Versionen 7.0 und 7.1 beigetragen.
  • Ich betreue 30 Plugins auf wordpress.org.
  • Ich organisiere WordCamps und trete dort als Speaker auf.
  • Ich war bei jedem WordCamp Europe von 2017 bis 2026 dabei.
  • Lokales WordPress-Meetup: WordPress Meetup Karlsruhe

Mein WordPress.org-Profil

Ich bin Mariusz Szatkowski und entwickle PHP-Anwendungen allein, von Gdynia in Polen aus. Ein Büro in Karlsruhe gibt es nicht, ich arbeite remote und schriftlich, ohne Termine und ohne Telefonate. Den Code schreibe ich selbst.

Diese Seite behandelt eine Frage, die sich in vielen Firmen erst nach dem ersten Ausfall stellt: Wie kommt eine neue Version einer PHP-Anwendung auf den Server, ohne dass jemand Dateien von Hand hochlädt? Sie lesen, in welchen Schritten ich ein Deployment aufbaue und was Sie davon prüfen können, auch wenn Sie nie mit mir arbeiten.

#Wo der FTP-Upload an seine Grenzen kommt

Die Symfony-Dokumentation nennt das Kopieren per FTP oder SCP die einfachste Art, eine Anwendung auszuliefern. Sie benennt auch den Nachteil: Während des Updates fehlt die Kontrolle über das System, und nach dem Übertragen sind weitere Schritte von Hand nötig. Das sind in der Praxis Composer, der Cache, die Datenbank und die Konfiguration.

Drei Dinge gehen bei diesem Vorgehen verloren:

  • Der Stand. Während die Dateien hochgeladen werden, laufen Besucher gegen eine Mischung aus alter und neuer Version.
  • Die Nachvollziehbarkeit. Nach einigen Monaten weiß niemand mehr, welcher Upload welche Änderung enthielt.
  • Der Rückweg. Wer die alte Version zurückbringen will, sucht die überschriebenen Dateien.

Ein wiederholbares Deployment ersetzt das durch eine feste Reihenfolge, die ein Skript ausführt: bauen, ablegen, umschalten, bei Fehlern zurück. Dieselbe Reihenfolge läuft jedes Mal gleich, auch wenn der Entwickler im Urlaub ist.

#Das Image: die offiziellen PHP-Images

Wenn PHP-Version und Erweiterungen in jeder Umgebung gleich sein sollen, bietet sich das offizielle Docker-Image php an (Docker Hub, hub.docker.com/_/php). Es gibt mehrere Varianten, und für eine Webanwendung sind zwei relevant:

  • php:<Version>-apache enthält den Apache-Webserver mit PHP als mod_php.
  • php:<Version>-fpm enthält PHP-FPM. Die Dokumentation verlangt dazu einen Reverse-Proxy wie Nginx, der FastCGI spricht. Außerdem warnt sie, den FPM-Port nicht mit --publish nach außen zu öffnen, weil das FastCGI-Protokoll nur in einem privaten Container-Netz sicher ist.

Die Variante -cli ist nach der Dokumentation für Skripte gedacht, nicht als Webserver. Ein Beispiel für ein Image mit FPM:

ARG PHP_VERSION
FROM php:${PHP_VERSION}-fpm

# Produktionskonfiguration statt Entwicklungswerte
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# Erweiterungen mit den mitgelieferten Hilfsskripten
RUN docker-php-ext-install -j"$(nproc)" opcache pdo_mysql

COPY . /var/www/html

Drei Punkte stehen so in der Dokumentation. Das Image liefert php.ini-development und php.ini-production mit, und die Produktionsdatei wird ausdrücklich empfohlen. Erweiterungen installieren die Hilfsskripte docker-php-ext-install, docker-php-ext-configure und docker-php-ext-enable. Und für die Leistung empfiehlt die Dokumentation OPcache in Produktionsumgebungen.

Die PHP-Version steht hier als Build-Argument, damit sie an einer Stelle festgelegt ist. Welche Version Sie nehmen sollten, hängt vom Supportzeitraum ab, den php.net auf der Seite supported-versions.php führt, nicht von dieser Seite.

Bei PECL-Erweiterungen verlangt die Dokumentation eine feste Versionsnummer im Aufruf, etwa pecl install redis-5.3.7, damit Sie wissen, wann sich eine Abhängigkeit ändert. Mehrere PECL-Erweiterungen sollen nacheinander installiert werden, nicht in einem Aufruf, damit ein Fehler auffällt.

#Composer im Build: ohne Entwicklungspakete, mit optimiertem Autoloader

Auf den Server gehört nur, was die Anwendung zum Laufen braucht. Die Symfony-Dokumentation nennt dafür diesen Aufruf:

composer install --no-dev --optimize-autoloader

Nach der Composer-Dokumentation überspringt --no-dev die Pakete aus require-dev, und die Autoloader-Erzeugung lässt die Regeln aus autoload-dev aus. Hinzu kommt: Gibt es eine composer.lock, installiert composer install genau die dort festgehaltenen Versionen, statt neu aufzulösen. Deshalb gehört die Lock-Datei ins Repository. Erst dadurch baut dasselbe Commit morgen dieselben Abhängigkeiten.

Zum Autoloader sagt die Composer-Dokumentation:

  • Stufe 1, Klassenkarte. --optimize-autoloader (kurz -o) wandelt die PSR-4-Regeln in eine Klassenkarte um, sodass für bekannte Klassen kein Zugriff auf das Dateisystem nötig ist. Dafür gibt es nach der Dokumentation keine echten Nachteile, und die Stufe soll in Produktion immer aktiv sein.
  • Stufe 2A, autoritative Klassenkarte. --classmap-authoritative geht weiter: Was nicht in der Karte steht, existiert nicht. Das scheitert, wenn Ihre Anwendung oder eine Abhängigkeit Klassen zur Laufzeit erzeugt. Die Dokumentation rät, die Option mit Vorsicht zu aktivieren.
  • Stufe 2B, APCu-Cache. --apcu-autoloader legt Treffer und Fehlschläge im APCu-Speicher ab. Dafür muss APCu auf dem Server vorhanden sein, und die Option lässt sich nicht mit 2A kombinieren.

In der Entwicklung sollten Sie nach der Dokumentation keine dieser Optimierungen einschalten, weil neue Klassen sonst nicht auftauchen.

#Konfiguration gehört nicht ins Repository

Zugangsdaten zur Datenbank, Schlüssel und Mail-Zugänge sind Sache der Umgebung. Die Symfony-Dokumentation nennt zwei gleichwertige Wege für die Produktion: echte Umgebungsvariablen (im Nginx, im Container oder über den Hoster) oder eine Datei mit den Produktionswerten auf dem Server. Wichtig ist die Warnung dazu: Der APP_SECRET darf nie ins Repository, und er muss über Deployments hinweg gleich bleiben, weil sich sonst signierte URLs und “Angemeldet bleiben”-Cookies ungültig machen.

Zwei Befehle aus der Dokumentation sparen Arbeit pro Anfrage:

  • Symfony: composer dump-env prod erzeugt eine optimierte .env.local.php, sodass die .env-Dateien nicht bei jeder Anfrage gelesen werden.
  • Laravel: php artisan config:cache fasst die Konfiguration in einer Datei zusammen. Danach wird die .env-Datei nicht mehr geladen, und env() liefert außerhalb der Konfigurationsdateien null. Laravel verlangt deshalb, env() nur in den Konfigurationsdateien aufzurufen.

Dazu kommt, was die Laravel-Dokumentation für Produktion festhält: APP_DEBUG muss dort false sein, sonst können Konfigurationswerte für Besucher sichtbar werden. Der Webserver soll auf den Ordner public zeigen. Wer index.php in die Projektwurzel verschiebt, legt laut Dokumentation viele sensible Dateien offen.

#Datenbankmigrationen im Deployment

Migrationen sind der heikelste Schritt, weil sie Daten betreffen und nicht nur Dateien. Beide großen Frameworks kennen den Ablauf.

Laravel fragt in Produktion vor zerstörerischen Operationen nach. Für den Einsatz in einem Skript gibt es php artisan migrate --force. Mit --pretend zeigt Laravel die SQL-Anweisungen, ohne sie auszuführen. Laufen mehrere Server gleichzeitig, verhindert --isolated, dass zwei Server dieselbe Migration starten: Laravel nimmt dann eine Sperre über den Cache-Treiber, der dafür auf allen Servern derselbe sein muss.

Doctrine Migrations (Symfony) kennt migrate --dry-run, das die SQL-Anweisungen anzeigt, und migrate --no-interaction für den Lauf ohne Rückfrage. --write-sql schreibt die Anweisungen in eine Datei, die Sie vorab prüfen oder von Ihrem Datenbankverantwortlichen einspielen lassen können. Die Dokumentation warnt, dass der Befehl version die Versionstabelle direkt ändert: Wer einen Eintrag löscht, führt diese Migration beim nächsten Lauf erneut aus.

Ob ein Deployment die Migration selbst ausführen soll, entscheide ich mit Ihnen pro Anwendung. Bei kleinen Schema-Änderungen ist das üblich. Bei großen Tabellen oder gleichzeitig laufenden Berichten plane ich die Migration als eigenen Schritt vor dem Umschalten, mit Sicherung der Datenbank davor.

Die Regel, die ich dabei einhalte: Die neue Migration darf die alte Codeversion nicht brechen. Spalten werden zuerst hinzugefügt und erst in einem späteren Release entfernt. Nur so bleibt ein Rollback des Codes möglich, während die Datenbank schon auf dem neuen Stand ist.

Das Prinzip kennt die PHP-Welt von Deployer. Die Dokumentation zeigt das Layout nach dem ersten Deployment:

~/example
  current -> releases/1     Symlink auf das aktuelle Release
  releases/                 ein Ordner pro Release
  shared/                   Dateien, die jedes Release teilt (z. B. .env)
  .dep/                     Deployer-Dateien

Der Webserver zeigt auf current/public. Ein neues Release wird in einen eigenen Ordner gebaut, mit Composer, Konfiguration und gemeinsamen Dateien, und erst am Ende zeigt der Symlink current auf den neuen Ordner. Besucher sehen dadurch nie einen halb kopierten Stand. Deployer nennt das “Zero downtime deployment”.

Nach der Deployer-Dokumentation gehört zu deploy:prepare unter anderem deploy:update_code, deploy:env, deploy:shared und deploy:writable, und deploy:publish setzt den Symlink, entsperrt und räumt alte Releases auf. Wie viele Releases bleiben, steht in keep_releases (Standard: 10).

Die Rezepte für Laravel und Symfony bringen die frameworkspezifischen Schritte mit. Das Laravel-Rezept führt unter anderem deploy:vendors, artisan:storage:link, artisan:optimize und artisan:migrate aus, das Symfony-Rezept deploy:vendors und deploy:cache:clear. In beiden Rezepten teilen sich die Releases ein Verzeichnis für Logs oder Uploads (Laravel: storage, Symfony: var/log).

Zwei Dinge sind nach dem Umschalten bei langlaufenden Prozessen zu beachten. Die Laravel-Dokumentation verlangt, Queue-Worker, Reverb oder Octane nach einem Deployment neu zu starten (php artisan reload), sonst läuft der alte Code weiter. Und PHP-FPM mit OPcache sollte nach dem Wechsel die neuen Dateien sehen; wie das bei Ihrer Serverkonfiguration geschieht, kläre ich vor dem ersten Deployment schriftlich.

#Rollback: der Weg zurück, bevor er gebraucht wird

Deployer führt einen Befehl rollback. Er setzt den Symlink auf das vorige Release und markiert das aktuelle als fehlerhaft, indem er eine Datei BAD_RELEASE mit Zeitstempel und Benutzer anlegt. Als Rollback-Ziel wird automatisch das letzte Release gewählt, das nicht als fehlerhaft markiert ist. Sie können es überschreiben, mit dep rollback -o rollback_candidate=123. Die Dokumentation nennt auch den Notweg von Hand: den Symlink current auf ein bestimmtes Release setzen.

Der Rollback betrifft den Code. Für die Datenbank gilt:

  • Laravel: php artisan migrate:rollback nimmt den letzten Stapel zurück, mit --step=5 eine begrenzte Anzahl, mit --batch=3 einen bestimmten Stapel. Auch hier zeigt --pretend vorab das SQL.
  • Doctrine Migrations: migrate prev geht eine Version zurück, migrate first bis vor die erste. Die Dokumentation warnt, dass diese Befehle Schemaänderungen und Datenverlust bedeuten können.

Eine Rückwärts-Migration, die Daten löscht, ist kein Rollback. Deshalb ist die Reihenfolge aus dem vorigen Abschnitt wichtiger als jeder Befehl: erst erweitern, später aufräumen.

#Was das Datenschutzrecht damit zu tun hat

Ein Deployment-Fehler kann personenbezogene Daten betreffen: eine offen liegende Konfigurationsdatei, ein aktiver Debug-Modus, eine Datenbank-Sicherung im öffentlichen Ordner. Zwei Artikel der DSGVO sind dafür relevant:

  • Nach Art. 32 sind geeignete technische und organisatorische Maßnahmen zu treffen. Zu ihnen zählt ausdrücklich die Fähigkeit, die Verfügbarkeit der Daten und den Zugang zu ihnen nach einem technischen Zwischenfall rasch wiederherzustellen, und ein Verfahren zur regelmäßigen Überprüfung der Wirksamkeit dieser Maßnahmen. Ein erprobter Rollback ist ein Teil davon.
  • Nach Art. 33 ist eine Verletzung des Schutzes personenbezogener Daten unverzüglich und möglichst binnen 72 Stunden der zuständigen Aufsichtsbehörde zu melden, es sei denn, sie führt voraussichtlich nicht zu einem Risiko für die Betroffenen. Wird die Frist überschritten, ist die Verzögerung zu begründen.

Zuständig ist in Baden-Württemberg der Landesbeauftragte für den Datenschutz und die Informationsfreiheit (LfDI BW). Auf seiner Website stehen Formulare für Beschwerden und für die Meldung einer Datenpanne. Ob in einem konkreten Fall gemeldet werden muss, beurteilen Ihr Datenschutzbeauftragter oder Ihre Rechtsberatung. Ich liefere die technischen Angaben: was wann auf den Server kam, welche Zugriffe das Protokoll zeigt und welcher Stand wiederhergestellt wurde.

#Karlsruhe: Betriebe, die ihre Software nicht nebenbei pflegen

Die Stadt Karlsruhe beschreibt auf ihrer Seite zu Gründerzentren und Netzwerken, dass die Gründerzentren auf Initiative der Stadt, der IHK Karlsruhe und der Handwerkskammer Karlsruhe entstanden sind. Genannt werden dort unter anderem das Gewerkshaus, ein neues Gründungs- und Gewerbezentrum für Handwerk und kleines produzierendes Gewerbe auf dem ehemaligen Michelin-Areal, der Handwerkerhof mit Büro-, Werkstatt- und Hallenflächen für junge Handwerksbetriebe und das FUX auf dem Gelände des Alten Schlachthofs für junge Unternehmen der Kultur- und Kreativwirtschaft.

Gebäude auf dem Gelände des Alten Schlachthofs in Karlsruhe

Foto: Achim Lammerts (Syntaxys), CC BY-SA 4.0, Wikimedia Commons.

Aus dieser Liste folgt für Ihre Software nichts Zwangsläufiges. Betriebe dieser Größe haben oft eine einzelne PHP-Anwendung, etwa ein Auftragsformular, ein Kundenportal oder einen Shop, die von einer Person aufgebaut wurde und per FTP gepflegt wird. Genau dort lohnt sich ein einfaches, festes Deployment mehr als eine große Plattform. Wer die Anwendung als WordPress-Seite betreibt, findet den passenden Weg auf der Seite zur WordPress-Wartung in Karlsruhe.

#Checkliste: das Deployment prüfen

FrageJa bedeutet
Liegt composer.lock im Repository?Dasselbe Commit baut dieselben Abhängigkeiten.
Läuft composer install --no-dev --optimize-autoloader im Build?Keine Testwerkzeuge in Produktion, schneller Autoloader.
Liegt die Konfiguration außerhalb des Repositories?Zugangsdaten stehen nicht in Git-Verlauf oder Image.
Ist APP_DEBUG in Produktion false?Fehlerseiten verraten keine Konfiguration.
Zeigt der Webserver auf public?Konfigurationsdateien sind nicht abrufbar.
Gibt es eine Sicherung der Datenbank vor jeder Migration?Ein Fehler in der Migration ist behebbar.
Bleibt die alte Codeversion mit dem neuen Schema lauffähig?Ein Rollback des Codes ist möglich.
Wurde der Rollback einmal auf der Testumgebung geübt?Er ist im Ernstfall kein Erstversuch.
Starten Queue-Worker nach dem Deployment neu?Kein alter Code läuft im Hintergrund weiter.

#Ablauf von der Anfrage bis zur Übergabe

  1. Anfrage. Sie schreiben mir über das Kontaktformular, wie die Anwendung heute ausgeliefert wird und was schon schiefgegangen ist.
  2. Fragen. Ich frage schriftlich nach Zugängen, PHP-Version, Framework, Hosting und Datenbank.
  3. Umfang. Ich schicke eine schriftliche Einschätzung mit Annahmen und Ausschlüssen.
  4. Testumgebung. Das Deployment läuft zuerst gegen eine Kopie, inklusive Rollback.
  5. Umstellung. Der erste Produktions-Deploy erfolgt in einem Zeitfenster, das Sie festlegen. Sie bekommen den Ablauf vorab schriftlich.
  6. Übergabe. Das Skript und eine kurze schriftliche Anleitung liegen in Ihrem Repository, nicht bei mir.

Layout, Mockups und Grafik liefern Sie selbst an. Ich setze die Anwendung um und kümmere mich um ihre Auslieferung.

#Häufige Fragen

#Warum reicht ein FTP-Upload für eine PHP-Anwendung nicht mehr?

Beim Hochladen läuft die Anwendung mit einem halben Stand weiter, und niemand kann später nachvollziehen, welche Dateien zu welcher Version gehören. Die Symfony-Dokumentation beschreibt genau das: Bei der Übertragung per FTP oder SCP fehlt die Kontrolle über das System, und Schritte wie Composer, Cache und Migrationen müssen von Hand folgen.

#Brauche ich Docker, um sauber zu deployen?

Nein. Ein Deployment mit Release-Ordnern und Symlink-Wechsel funktioniert auch auf einem klassischen Server per SSH. Docker lohnt sich, wenn die PHP-Version und die Erweiterungen in Entwicklung, Test und Produktion identisch sein sollen, weil sie dann im Image festgeschrieben sind.

#Was passiert mit der Datenbank, wenn ich zurückrolle?

Der Symlink-Wechsel macht den Code rückgängig, nicht die Daten. Deshalb gestalte ich Migrationen so, dass die alte Version mit dem neuen Schema weiterläuft, und halte für den Notfall die Rückwärts-Migration bereit. Doctrine Migrations und Laravel kennen beide den Weg zurück.

#Wohin gehören Passwörter und Zugangsdaten?

Nicht ins Repository und nicht in das Image. Die Symfony-Dokumentation nennt als Optionen echte Umgebungsvariablen oder eine Datei mit den Produktionswerten auf dem Server, und Deployer teilt eine .env-Datei über alle Releases.

#Muss ein Datenleck nach einem Fehler beim Deployment gemeldet werden?

Wenn eine Verletzung des Schutzes personenbezogener Daten vorliegt, die voraussichtlich ein Risiko für die Betroffenen bedeutet, ja: nach Art. 33 DSGVO unverzüglich und möglichst binnen 72 Stunden an die zuständige Aufsichtsbehörde. In Baden-Württemberg ist das der LfDI BW. Ob eine Meldepflicht besteht, klärt Ihr Datenschutzbeauftragter oder Ihre Rechtsberatung, nicht ich.

#Gibt es Termine in Karlsruhe oder Telefonate?

Nein. Von der Anfrage bis zur Abnahme läuft alles schriftlich, auch die Rückfragen und der Zugang zum Server. Jede Entscheidung bleibt nachlesbar.

#Ihre Anfrage an mich

Schreiben Sie mir über das Kontaktformular. Nennen Sie die Adresse der Anwendung oder des Repositories, ihren Zweck, Ihren Termin und den Stand: Wie wird heute ausgeliefert, welche PHP-Version und welches Framework laufen, wo liegt der Server. Ich antworte schriftlich, zuerst mit offenen Fragen, danach mit einer Einschätzung des Umfangs. Die erste Einschätzung bekommen Sie schriftlich und ohne Kosten, eine Verpflichtung entsteht daraus nicht. Mein gesamtes PHP-Angebot finden Sie unter PHP-Entwickler.

Brauchen Sie die Leistung: PHP-Entwickler in Karlsruhe?

Beschreiben Sie Ihr Projekt per E-Mail. Antwort innerhalb von 48 Stunden.

Projekt beschreiben

Weitere WordPress-Dienste und Wissensbasis entdecken