Der LEMP-Stack repräsentiert eine der bewährtesten Architekturen für den Betrieb dynamischer Web-Anwendungen unter Linux. Linux bildet die stabile Grundlage, Nginx den effizienten Webserver, MariaDB den relationalen Datenbankserver und PHP die serverseitige Skriptsprache. Diese vier Komponenten ergänzen sich präzise und schaffen eine Plattform, die hohe Performance mit niedrigem Ressourcenverbrauch verbindet. Du setzt diesen Stack ein, wenn du PHP-basierte Lösungen wie Content-Management-Systeme, APIs oder individuelle Webanwendungen betreiben möchtest.
Im Vergleich zu anderen Konfigurationen zeichnet er sich durch seine Skalierbarkeit und Wartbarkeit aus. Nginx verarbeitet Anfragen asynchron und bewältigt hohe Lasten besser als viele Alternativen. MariaDB gewährleistet Kompatibilität und Stabilität bei der Datenhaltung. PHP liefert moderne Features für die serverseitige Logik. Zusammen bilden sie die Basis für den Alltag von Administratoren, die zuverlässige Web-Umgebungen aufbauen und betreiben müssen.
In diesem Artikel baust du den vollständigen LEMP-Stack auf einer frischen Ubuntu 24.04 LTS-Installation auf. Die Anleitung führt dich schrittweise durch Vorbereitung, Installation, Konfiguration und Absicherung. Du erhältst nicht nur die notwendigen Befehle, sondern auch die technischen Hintergründe zu jeder Entscheidung. Das Verständnis der Zusammenhänge ermöglicht es dir, den Stack später eigenständig zu warten, zu optimieren oder an spezifische Anforderungen anzupassen.
💡 Die LTS-Version von Ubuntu bietet langfristigen Support und eine konsistente Paketbasis, die für produktive Umgebungen unerlässlich ist und langfristige Planung erleichtert.
Der Artikel ist bewusst praxisorientiert gestaltet. Jeder Abschnitt erklärt, was technisch geschieht, warum die Maßnahme relevant ist und worauf du in der täglichen Administration achten musst. Du erkennst die Abhängigkeiten zwischen den Diensten und lernst, wie du sie sicher und effizient betreibst. Die klare Struktur macht den Text zu einem Nachschlagewerk, das du auch später noch konsultieren kannst.
❗ Viele Junior-Administratoren unterschätzen die Bedeutung einer sauberen Systemvorbereitung. Ohne diese Grundlage entstehen später Konflikte bei der Dienstintegration, die nur mit zusätzlichem Aufwand zu beheben sind.
Als Junior Linux-Systemadministrator oder Junior Linux-Specialist baust du hier gezielt deine Kompetenzen aus. Du bringst bereits Linux-Systemverständnis mit, kennst die Grundlagen der Systemadministration und bewegst dich sicher in der Shell. Der Leitfaden vertieft dein Wissen in der Webserver-Administration und gibt dir Werkzeuge für reale Szenarien an die Hand. Einsteiger in die Linux-Systemadministration finden ebenso wertvolle Orientierung.
Der Inhalt konzentriert sich auf nachhaltige Praktiken, die du direkt in deiner Infrastruktur anwenden kannst. Du erfährst, welche Aspekte bei der Einrichtung besonders kritisch sind und wie du typische Fallstricke von vornherein vermeidest. Die didaktische Aufbereitung sorgt dafür, dass du nicht nur mechanisch arbeitest, sondern die Prinzipien wirklich verstehst und später selbstständig Entscheidungen treffen kannst.
⚠️ Wichtig: Dieser Artikel richtet sich an Linux-Administratoren und IT-Fachkräfte, die bereits grundlegende Erfahrungen mit Ubuntu und der Kommandozeile haben. Du solltest dich im Terminal sicher bewegen können und verstehen, was Paketverwaltung bedeutet.
Bevor du mit der Installation beginnst, bereiten wir das Ubuntu-System optimal vor.
Vorbereitung
Du beginnst jetzt die konkrete Vorbereitung deines Ubuntu-Systems.
Die erste und wichtigste Maßnahme ist die Aktualisierung der Paketquellen und die Installation aller verfügbaren Sicherheits- und Funktionsupdates. Das stellt sicher, dass dein frisch installiertes System mit den neuesten Paketinformationen und Versionen arbeitet, bevor du den LEMP-Stack aufbaust.
Was passiert hier genau? Der Befehl apt update kontaktiert die in /etc/apt/sources.list und den Dateien unter /etc/apt/sources.list.d/ definierten Repositories. Er lädt die aktuellen Paketlisten, Versionsnummern und Abhängigkeiten herunter und speichert sie lokal im Cache. Danach weißt du exakt, welche Versionen von Nginx, MariaDB oder PHP verfügbar sind. Ohne diesen Schritt würde apt install veraltete Metadaten verwenden und möglicherweise Pakete installieren, die bereits Sicherheitslücken aufweisen.
Warum machen wir das? Ubuntu 24.04 erhält kontinuierlich Updates aus den offiziellen Canonical-Repositories. Diese Updates schließen Sicherheitslücken, beheben Bugs und optimieren die Performance der Systembibliotheken. Du vermeidest so von Anfang an Konflikte, die später bei der Installation von Diensten auftreten können.
Warum ist das wichtig? Ein nicht aktualisiertes System birgt Risiken. Veraltete Pakete können zu Abhängigkeitsfehlern führen, wenn du später PHP-Erweiterungen oder MariaDB-Module hinzufügst. Außerdem läuft dein Server dann nicht auf dem aktuellen Sicherheitsstand – ein kritischer Punkt für jede produktive Web-Umgebung.
Zuerst meldest du dich im Terminal an. Arbeite am besten als Benutzer mit sudo-Rechten oder direkt als root. Gib den folgenden Befehl ein, um die Paketlisten zu aktualisieren:
sudo apt update
Der Befehl gibt eine Liste der aktualisierten Repositories aus und endet mit Reading package lists... Done. Du siehst, wie viele Pakete betroffen sind. Führe ihn immer zuerst aus, bevor du weitere apt-Befehle nutzt.
Danach installierst du die verfügbaren Updates. Der Befehl apt upgrade prüft den Cache und ersetzt ältere Pakete durch neuere Versionen, ohne die Gesamtkonfiguration zu verändern. Die Option -y bestätigt alle Rückfragen automatisch:
sudo apt upgrade -y
Was passiert hier? Das System lädt die Update-Pakete herunter, entpackt sie und installiert sie. Kernel-Updates oder Bibliotheksänderungen können später einen Neustart erfordern.
💡 Führe
sudo apt updateregelmäßig vor jeder größeren Installation aus – das spart dir später Stunden bei der Fehlersuche durch Versionskonflikte.
Nach dem Upgrade räumst du das System auf. Mit autoremove entfernt apt nicht mehr benötigte Abhängigkeiten, die bei vorherigen Updates entstanden sind:
sudo apt autoremove -y
Und mit autoclean löschst du heruntergeladene Paketdateien aus dem Cache, die nicht mehr aktuell sind:
sudo apt autoclean
Diese beiden Befehle halten den Speicherplatz frei und verhindern, dass der Cache über die Zeit unnötig wächst.
🔧 Praktisches Beispiel
Hier siehst du die komplette, praxisnahe Sequenz, die du genau so in dein Terminal kopieren kannst.
Die Kommentare erklären jede Zeile:
# 1. Paketlisten aktualisieren
sudo apt update
# 2. Alle verfügbaren Updates einspielen
sudo apt upgrade -y
# 3. Nicht mehr benötigte Pakete entfernen
sudo apt autoremove -y
# 4. Alten Cache aufräumen
sudo apt autoclean
# 5. Optional: Vollständige Systemaktualisierung (bei Kernel-Änderungen)
sudo apt full-upgrade -y
Führe diese fünf Zeilen nacheinander aus. Nach dem ersten apt update prüfst du mit cat /etc/apt/sources.list, ob die Haupt-Repositories (main, universe, restricted, multiverse) aktiviert sind. In einer Standard-Ubuntu-24.04-Installation sind sie bereits korrekt konfiguriert. Falls du später ein PPA hinzufügen musst, fügst du es erst nach diesem Schritt hinzu.
❗ Viele Einsteiger überspringen das Update und installieren direkt Dienste. Das führt später zu Fehlermeldungen wie „Depends: php8.3-fpm but it is not installable“, weil der Paketindex veraltet ist. Immer erst aktualisieren – das erspart dir solche Überraschungen.
Zusätzlich kannst du den Status deiner Repositories mit einem einzelnen Befehl überprüfen:
sudo apt policy
Die Ausgabe zeigt dir die Prioritäten der Quellen und bestätigt, dass alles sauber läuft.
Du hast jetzt ein vollständig aktualisiertes System mit optimierten Paketquellen. Der Cache ist aktuell, alle Sicherheitsupdates sind eingespielt und unnötige Dateien entfernt. Das bildet die stabile Grundlage für die weiteren Schritte.
Mit einem aktualisierten System kannst du nun zur nächsten Phase der Vorbereitung übergehen.
Firewall (UFW) einrichten
Du hast dein System gerade aktualisiert und die Paketquellen optimiert. Jetzt richtest du die Firewall ein, damit nur erlaubter Traffic auf deinen Server gelangt. Ubuntu bringt UFW mit – die Uncomplicated Firewall. Sie ist ein benutzerfreundliches Frontend für nftables und schützt dein System vor unerwünschten Zugriffen, ohne dass du komplexe Regeln von Hand schreiben musst.
Was passiert hier? UFW verwaltet iptables- oder nftables-Regeln im Hintergrund. Mit wenigen Befehlen definierst du, welche Ports offen sind. Standardmäßig blockiert UFW alle eingehenden Verbindungen und lässt ausgehende frei. Du fügst gezielt Ausnahmen hinzu, zum Beispiel für SSH, damit du weiterhin remote arbeiten kannst.
Warum machen wir das? Ein offener Server ohne Firewall ist ein leichtes Ziel für Scans und Angriffe. UFW sorgt dafür, dass nur die Dienste erreichbar sind, die du später wirklich brauchst. Du reduzierst die Angriffsfläche von Anfang an und verhinderst, dass ungenutzte Ports offen bleiben.
Warum ist das wichtig? In einer produktiven Umgebung entscheidet die Firewall darüber, ob dein Server sicher bleibt. Ohne sie könnte jeder Port 22 oder spätere Web-Ports direkt aus dem Internet erreicht werden. Mit UFW hast du eine klare, nachvollziehbare Schicht, die du später einfach erweitern kannst.
Zuerst prüfst du, ob UFW bereits installiert ist. Auf einer frischen Ubuntu 24.04 LTS ist es meist vorhanden, aber du stellst es sicher:
sudo apt install ufw -y
Der Befehl installiert das Paket und seine Abhängigkeiten, falls nötig. Danach setzt du die Standardrichtlinien. Diese Einstellung blockiert alles Eingehende und lässt Ausgehendes frei – ein sicherer Ausgangspunkt:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Du erlaubst jetzt SSH, damit du nicht ausgeschlossen wirst, sobald die Firewall aktiv wird. Der Port 22 (oder dein eigener SSH-Port) muss immer zuerst freigeschaltet werden:
sudo ufw allow ssh
UFW erkennt den Dienst automatisch über /etc/services und fügt die Regel für TCP-Port 22 hinzu. Du kannst alternativ sudo ufw allow 22/tcp schreiben, falls du einen anderen Port nutzt.
💡 Bevor du die Firewall aktivierst, prüfe mit
sudo ufw statusden aktuellen Zustand. So siehst du sofort, ob alle notwendigen Regeln vorhanden sind.
Nun aktivierst du UFW. Der Befehl startet den Dienst und lädt die Regeln:
sudo ufw enable
UFW fragt nach einer Bestätigung. Gib y ein. Der Firewall-Dienst startet sofort und bleibt nach einem Neustart aktiv. Du erhältst die Meldung, dass die Firewall aktiviert wurde.
Überprüfe den Status mit detaillierter Ausgabe:
sudo ufw status verbose
Die Ausgabe zeigt die aktiven Regeln, die Standardrichtlinien und ob IPv6 aktiviert ist. Du siehst zum Beispiel:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
Als grundlegende Sicherheitsmaßnahme installierst du zusätzlich Fail2ban. Dieses Tool liest Log-Dateien und blockt IPs automatisch nach mehrmaligen fehlgeschlagenen Anmeldeversuchen. Es ergänzt UFW perfekt, weil es dynamische Bans über UFW-Regeln setzt.
sudo apt install fail2ban -y
Fail2ban ist sofort aktiv. Die Standardkonfiguration schützt bereits SSH. Du kannst später eigene Jails für Nginx oder MariaDB hinzufügen, aber für den Anfang reicht die Grundkonfiguration.
❗ Viele Administratoren aktivieren UFW, ohne vorher SSH zu erlauben. Das führt dazu, dass du dich nicht mehr einloggen kannst und den Server nur noch über die Konsole oder einen Provider-Notfallzugriff erreichst. Immer zuerst SSH freischalten – das ist der häufigste und teuerste Fehler.
🔧 Praktisches Beispiel Hier die komplette, praxiserprobte Sequenz, die du exakt so ausführen kannst.
Die Kommentare erklären jede Zeile:
# UFW installieren (falls noch nicht vorhanden)
sudo apt install ufw -y
# Standardrichtlinien setzen: alles eingehend blocken, ausgehend erlauben
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH-Verbindung explizit erlauben (unbedingt zuerst!)
sudo ufw allow ssh
# Firewall aktivieren
sudo ufw enable
# Status prüfen
sudo ufw status verbose
# Fail2ban für automatischen Brute-Force-Schutz installieren
sudo apt install fail2ban -y
# Fail2ban-Status überprüfen
sudo systemctl status fail2ban
Führe die Befehle der Reihe nach aus. Nach ufw enable testest du sofort eine neue SSH-Verbindung in einem zweiten Fenster, um sicherzustellen, dass du nicht ausgesperrt bist. Mit Fail2ban aktiviert sich der Schutz für SSH automatisch über die Jail-Konfiguration in /etc/fail2ban/jail.local.
Du kannst später mit sudo ufw allow 'Nginx Full' weitere Ports für den Webserver freigeben, sobald die Dienste laufen. Die aktuelle Konfiguration reicht aber vollkommen aus, um dein System während der weiteren Vorbereitung abzusichern.
Mit der aktivierten Firewall und Fail2ban als zusätzlichem Schutz bist du gegen die häufigsten Angriffsvektoren gewappnet. Als Nächstes überprüfst du die benötigten Abhängigkeiten und Voraussetzungen.
Benötigte Abhängigkeiten
Du hast die Firewall mit UFW aktiviert und Fail2ban eingerichtet. Nun prüfst du alle benötigten Abhängigkeiten und Voraussetzungen, bevor die eigentlichen LEMP-Komponenten installiert werden. Dieser Schritt stellt sicher, dass dein Server technisch bereit ist und keine Konflikte oder Engpässe später die Installation behindern.
Was passiert hier? Du führst eine Reihe von Diagnosebefehlen aus, die den aktuellen Zustand deines Systems abfragen. Diese Befehle lesen Systeminformationen aus dem Kernel, dem Dateisystem und den laufenden Prozessen. Du erhältst klare Auskünfte über Ressourcen, Konfigurationen und mögliche Konflikte. So erkennst du frühzeitig, ob Anpassungen nötig sind.
Warum machen wir das? Eine LEMP-Installation belastet CPU, RAM und Speicher. Nginx, MariaDB und PHP-FPM laufen parallel und verarbeiten Anfragen gleichzeitig. Ohne ausreichende Ressourcen kommt es zu Performance-Einbußen oder Abstürzen. Du vermeidest außerdem, dass bereits installierte Dienste wie Apache mit Nginx kollidieren.
Warum ist das wichtig? In der Praxis entscheidet dieser Check darüber, ob dein Stack stabil läuft. Ein Server mit zu wenig RAM swappt ständig und wird langsam. Fehlende Abhängigkeiten führen zu Fehlermeldungen bei der späteren Installation. Du baust hier die Grundlage für einen zuverlässigen Betrieb, der auch unter Last funktioniert.
Zuerst überprüfst du den verfügbaren Festplattenspeicher. Der Befehl zeigt dir, wie viel Platz auf den Partitionen frei ist:
df -h
Die Ausgabe listet alle Dateisysteme auf. Achte darauf, dass die Root-Partition / mindestens 20 GB frei hat. Für eine produktive Umgebung mit Logs und Datenbanken sind 40 GB oder mehr empfehlenswert. Wenn weniger Platz vorhanden ist, räume zuerst mit sudo apt autoclean auf oder erweitere den Datenträger.
Als Nächstes prüfst du den Arbeitsspeicher und den Swap-Bereich:
free -h
Hier siehst du den aktuellen RAM und ob Swap konfiguriert ist. Für LEMP solltest du mindestens 2 GB RAM haben. Bei weniger als 1 GB RAM installiere später Swap, um Engpässe zu vermeiden.
💡 Mit
free -herkennst du sofort, ob dein Server unter Speichermangel leidet. Führe den Befehl nach einem Neustart aus, damit die Werte repräsentativ sind.
Du kontrollierst nun den Hostnamen und die Netzwerkkonfiguration. Ein eindeutiger Hostname hilft später bei der Verwaltung mehrerer Server:
hostnamectl
Die Ausgabe enthält Static hostname und Operating System. Stelle sicher, dass der Hostname sinnvoll ist, zum Beispiel webserver01. Überprüfe zusätzlich die IP-Adresse:
ip addr show
Suche nach der Schnittstelle eth0 oder ens3 und notiere die IPv4-Adresse. Diese brauchst du später für Tests und Firewall-Regeln.
Die Zeitzone und die Uhrzeit müssen stimmen, damit Logs korrekt sind und Zertifikate funktionieren:
timedatectl
Die Ausgabe zeigt Time zone und NTP service: active.
Ist NTP nicht aktiv, synchronisiere mit:
sudo timedatectl set-timezone Europe/Berlin
sudo systemctl restart systemd-timesyncd
Du prüfst, ob bereits ein Webserver wie Apache läuft. Dieser würde mit Nginx kollidieren und Port 80 blockieren:
sudo systemctl status apache2
Falls der Dienst aktiv ist, deaktiviere ihn:
sudo systemctl stop apache2
sudo systemctl disable apache2
sudo apt purge apache2 -y
Das entfernt Apache vollständig und verhindert Konflikte.
AppArmor, die Sicherheitsrichtlinie von Ubuntu, sollte aktiv sein und später für die Dienste konfiguriert werden. Überprüfe den Status:
sudo aa-status
Die Ausgabe listet geladene Profile. Für den Anfang reicht es, wenn der Dienst läuft.
Du installierst nun fehlende Basispakete, die für die spätere Installation nützlich sind. Dazu gehören Tools wie curl für Downloads und unzip für Archive:
sudo apt install curl wget unzip software-properties-common -y
Diese Pakete sind klein und werden häufig benötigt, wenn du später Module oder Zertifikate einrichtest.
❗ Manche Administratoren überspringen den Check auf
Apacheund installierenNginxdirekt. Dann hört der Webserver nicht oder bindet nicht anPort 80. Immer zuerst bestehende Dienste prüfen und entfernen.
Du überprüfst außerdem die installierten PHP-Versionen, falls schon welche vorhanden sind. Ubuntu 24.04 bringt PHP 8.3 mit, aber du stellst sicher, dass keine älteren Versionen stören:
apt list --installed | grep php
Falls ältere Pakete auftauchen, entferne sie mit sudo apt purge php* -y.
🔧 Praktisches Beispiel
Hier die vollständige Checkliste als ausführbares Skript. Kopiere es in eine Datei check-prerequisites.sh, mache sie ausführbar mit chmod +x check-prerequisites.sh und starte sie mit ./check-prerequisites.sh.
Die Kommentare erklären jede Zeile genau, sodass du jede Ausgabe sofort verstehst:
#!/bin/bash
echo "=== Systemressourcen prüfen ==="
df -h | grep -E 'Filesystem|/'
echo ""
free -h
echo ""
echo "=== Hostname und Netzwerk ==="
hostnamectl
echo ""
ip addr show | grep -E 'inet |link/'
echo ""
echo "=== Zeitzone und Zeit ==="
timedatectl
echo ""
echo "=== Konflikte prüfen (Apache) ==="
if systemctl is-active --quiet apache2; then
echo "Apache läuft – wird gestoppt und entfernt."
sudo systemctl stop apache2
sudo systemctl disable apache2
sudo apt purge apache2* -y
else
echo "Kein Apache gefunden – gut."
fi
echo ""
echo "=== AppArmor Status ==="
sudo aa-status | head -n 10
echo ""
echo "=== Basispakete installieren ==="
sudo apt install curl wget unzip software-properties-common -y
echo ""
echo "=== PHP-Vorinstallation prüfen ==="
apt list --installed | grep php || echo "Keine PHP-Version vorinstalliert."
echo "Alle Checks abgeschlossen."
Führe das Skript aus. Es gibt dir eine klare Übersicht und behebt kleine Probleme automatisch. Nach dem Lauf siehst du, ob alles grün ist oder ob du noch Hand anlegen musst.
Du hast jetzt alle Abhängigkeiten und Voraussetzungen systematisch geprüft. Dein Server ist ressourcentechnisch bereit, konfliktfrei und mit den notwendigen Basistools ausgestattet.
Mit den geklärten Voraussetzungen kannst du zur Installation der LEMP-Komponenten übergehen.
Installation LEMP
Nginx als Webserver installieren
Nachdem du alle Voraussetzungen geprüft und das System vorbereitet hast, installierst du nun den ersten Baustein des LEMP-Stacks: cNginx als Webserver.
Der Befehl lädt das Paket aus den offiziellen Ubuntu-Repositories und richtet den Dienst sofort ein.
sudo apt install nginx
ruft apt die Paketlisten ab, löst Abhängigkeiten auf und installiert den Nginx-Binary zusammen mit den notwendigen Konfigurationsdateien. Der Dienst wird automatisch gestartet und auf Port 80 gebunden. Du erhältst eine lauffähige Webserver-Instanz, die sofort Anfragen entgegennehmen kann.
Warum machen wir das? Nginx ist leichtgewichtig, verarbeitet Anfragen asynchron und verbraucht deutlich weniger Ressourcen als Alternativen wie Apache. Du legst damit die Grundlage für schnelle, stabile Webseiten, die später mit PHP und MariaDB zusammenarbeiten. Die Installation aus den Ubuntu-Repos garantiert Kompatibilität und einfache Updates über das Paketmanagement.
Warum ist das wichtig? Ein fehlerfrei installierter Webserver verhindert spätere Konflikte bei der Integration von PHP-FPM. Du hast sofort eine funktionierende Testumgebung und kannst den gesamten Stack schrittweise aufbauen, ohne dass ein defekter Dienst den Rest blockiert.
Zuerst aktualisierst du noch einmal die Paketlisten, obwohl du das bereits bei der Vorbereitung getan hast. Das schadet nie und stellt sicher, dass du die aktuelle Version bekommst:
sudo apt update
Danach installierst du Nginx mit der -y-Option, damit alle Rückfragen automatisch bestätigt werden:
sudo apt install nginx -y
Der Befehl zeigt den Fortschritt und installiert zusätzlich Abhängigkeiten wie libnginx-mod-http-*. Sobald er fertig ist, startet Nginx automatisch. Du siehst in der Ausgabe, dass das Paket nginx und nginx-core eingerichtet wurden.
Überprüfe sofort, ob der Dienst läuft.
Der Befehl zeigt den aktuellen Status:
sudo systemctl status nginx
Die Ausgabe enthält Active: active (running) und die Prozess-ID. Das bestätigt, dass der Webserver betriebsbereit ist. Du kannst den Dienst auch manuell starten oder stoppen, falls nötig:
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
Aktiviere den automatischen Start nach einem Neustart des Servers:
sudo systemctl enable nginx
Das sorgt dafür, dass Nginx nach jedem Booten sofort verfügbar ist – essenziell für einen produktiven Server.
Nun öffnest du die Firewall für den Web-Traffic.
UFW hat ein vordefiniertes Profil für Nginx:
sudo ufw allow 'Nginx Full'
sudo ufw reload
Die Regel erlaubt Port 80 (HTTP) und Port 443 (HTTPS). Du siehst in sudo ufw status die neue Zeile Nginx Full ALLOW Anywhere.
Teste die Installation direkt im Browser oder mit curl. Öffne http://deine-server-ip und du solltest die Standard-Willkommensseite von Nginx sehen. Mit curl prüfst du das ohne grafische Oberfläche:
curl -I http://localhost
Die Antwort HTTP/1.1 200 OK zeigt, dass alles funktioniert.
💡 Nach der Installation schau dir die Verzeichnisstruktur an, um später gezielt zu konfigurieren.
Die Hauptkonfigurationsdatei liegt unter /etc/nginx/nginx.conf. Die aktiven Websites findest du in /etc/nginx/sites-available/ und den aktivierten Links in /etc/nginx/sites-enabled/. Hier ein einfaches ASCII-Diagramm der wichtigsten Pfade:
/
└── etc/
└── nginx/
├── nginx.conf ← globale Konfiguration
├── sites-available/ ← Vorlagen für virtuelle Hosts
├── sites-enabled/ ← aktive Symlinks
└── conf.d/ ← zusätzliche Module
Du kannst die Standardkonfiguration mit sudo nginx -t testen. Der Befehl prüft die Syntax und gibt syntax is ok aus, wenn alles korrekt ist.
sudo nginx -t
Das ist ein wichtiger Sicherheits-Check, bevor du Änderungen übernimmst
❗ Ein häufiger Fehler ist, dass der Dienst nicht startet, weil Port 80 bereits von einem anderen Prozess belegt ist. Prüfe mit
sudo ss -tuln | grep 80und beende den Konflikt sofort.
Du kannst den Log-Ordner einsehen, um Fehler direkt zu erkennen:
sudo tail -f /var/log/nginx/error.log
So beobachtest du in Echtzeit, was passiert, wenn du später Konfigurationen änderst.
🔧 Praktisches Beispiel
Hier die komplette, praxiserprobte Installationssequenz, die du exakt so in dein Terminal kopieren kannst. Die Kommentare erklären jede Zeile, damit du den Ablauf verstehst und bei Bedarf anpassen kannst:
# 1. Paketlisten aktualisieren (sicherheitshalber)
sudo apt update
# 2. Nginx installieren
sudo apt install nginx -y
# 3. Dienststatus prüfen
sudo systemctl status nginx
# 4. Automatischen Start aktivieren
sudo systemctl enable nginx
# 5. Firewall für Web-Traffic öffnen
sudo ufw allow 'Nginx Full'
sudo ufw reload
# 6. Konfiguration testen
sudo nginx -t
# 7. Schneller Test mit curl
curl -I http://localhost
Führe die Zeilen der Reihe nach aus. Nach dem letzten Befehl siehst du die HTTP-Header der Standardseite. Das Skript ist so aufgebaut, dass du es bei Fehlern einfach wiederholen kannst. Speichere es als install-nginx.sh und mache es ausführbar mit chmod +x install-nginx.sh.
Du hast Nginx nun vollständig installiert, aktiviert und getestet. Der Webserver lauscht auf Port 80 und ist bereit für die nächsten Komponenten.
Mit dem laufenden Nginx kannst du nun den nächsten Baustein des Stacks einrichten.
## Installation der LEMP-Komponenten
MariaDB installieren
Kommen wir nun zum nächsten Herzstück des LEMP-Stacks. Nachdem du Nginx erfolgreich installiert und getestet hast, kommt nun der Datenbankserver MariaDB ins Spiel. Dieser übernimmt die Rolle des relationalen Speichers und bildet die Grundlage für dynamische Anwendungen, die später mit PHP zusammenarbeiten.
Was passiert hier? Der Befehl sudo apt install mariadb-server holt das Paket aus den offiziellen Ubuntu-Repositories, installiert den Server-Daemon, die Client-Tools und die notwendigen Bibliotheken. Der Dienst startet danach automatisch und lauscht standardmäßig auf Port 3306. Gleichzeitig legt das System eine erste root-Instanz an, die du sofort absichern musst.
Warum machen wir das? MariaDB ist kompatibel zu MySQL, aber vollständig open-source und optimiert für Performance in Web-Umgebungen. Du erhältst einen stabilen Datenbankserver, der später PHP-Anfragen effizient verarbeitet und Transaktionen zuverlässig speichert. Die Installation aus den Standard-Repos garantiert, dass alles nahtlos mit dem restlichen Stack zusammenpasst.
Warum ist das wichtig? Ohne gesicherte Datenbank bleibt dein gesamter LEMP-Stack angreifbar. Standardeinstellungen wie anonyme Benutzer oder die Test-Datenbank sind bekannte Schwachstellen. Du legst hier den Grundstein für sichere, produktive Datenhaltung und vermeidest spätere aufwändige Nachbesserungen.
Zuerst aktualisierst du die Paketlisten ein letztes Mal, damit du die aktuelle Version aus den Repositories bekommst:
sudo apt update
Danach installierst du den MariaDB-Server:
sudo apt install mariadb-server -y
Der Befehl lädt mehrere Pakete herunter und richtet sie ein. Sobald er fertig ist, läuft der Dienst bereits. Du siehst in der Ausgabe mariadb-server und mariadb-common als installiert.
Überprüfe sofort den Status des Dienstes:
sudo systemctl status mariadb
Eine typische Ausgabe sieht so aus:
● mariadb.service - MariaDB 10.11.14 database server
Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
Active: active (running) since ...
Der Dienst ist aktiv und bereit.
Aktiviere ihn für den automatischen Start nach einem Server-Neustart:
sudo systemctl enable mariadb
Das sorgt dafür, dass MariaDB nach jedem Boot direkt verfügbar ist.
💡 Führe
sudo systemctl status mariadbregelmäßig aus, wenn du später Änderungen vornimmst. So erkennst du sofort, ob der Dienst läuft oder Probleme hat.
Nun sicherst du die Installation. Der Befehl mysql_secure_installation ist ein interaktives Skript, das die wichtigsten Schwachstellen schließt. Starte es mit:
sudo mysql_secure_installation
Das Skript fragt dich Schritt für Schritt ab. Hier die detaillierte Erklärung jedes Schrittes, damit du genau weißt, was du tust:
- Enter current password for root (enter for none): Drücke einfach
Enter, da noch kein Passwort gesetzt ist. - Set root password? [Y/n]: Gib
Yein und lege ein starkes Passwort fest. - Remove anonymous users? [Y/n]: Gib
Yein. Anonyme Benutzer sind ein Sicherheitsrisiko. - Disallow root login remotely? [Y/n]: Gib
Yein. Root darf nur lokal zugreifen. - Remove test database and access to it? [Y/n]: Gib
Yein. Die Test-Datenbank ist nur für Experimente gedacht. - Reload privilege tables now? [Y/n]: Gib
Yein. Die Änderungen werden sofort wirksam.
Warum ist das wichtig? Diese wenigen Minuten machen den Unterschied zwischen einem offenen System und einer abgesicherten Produktionsdatenbank. Du entfernst alle Standardfallen, die Angreifer sonst sofort ausnutzen.
Nach dem Skript kannst du den Login testen.
Starte den MariaDB-Client mit deinem neuen root-Passwort:
mysql -u root -p
Gib das Passwort ein. Du landest in der MariaDB-Shell und siehst MariaDB [(none)]>. Verlasse sie wieder mit exit.
Die Konfigurationsdateien liegen unter /etc/mysql/. Die wichtigste Datei für globale Einstellungen ist mariadb.conf.d/50-server.cnf.
Hier ein Diagramm der Struktur:
/
└── etc/
└── mysql/
├── mariadb.conf.d/
│ ├── 50-server.cnf ← Hauptkonfiguration
│ └── 60-galera.cnf
└── my.cnf ← Symlink zur Einbindung
Du kannst später in 50-server.cnf Bind-Adresse, Port oder Performance-Parameter anpassen.
❗ Vergiss nicht, das root-Passwort an einem sicheren Ort zu speichern. Wenn du es verlierst, musst du den Server im Recovery-Modus zurücksetzen – das ist aufwändig und unterbricht den Betrieb.
Du kannst den Port und die Bind-Adresse schnell prüfen:
sudo ss -tuln | grep 3306
Die Ausgabe sollte 0.0.0.0:3306 oder 127.0.0.1:3306 zeigen, je nach Konfiguration.
Die Log-Dateien helfen bei der Fehlersuche:
sudo tail -n 50 /var/log/mysql/error.log
Hier siehst du Start-Meldungen und eventuelle Warnungen direkt.
🔧 Praktisches Beispiel
Hier die komplette, schrittweise Installations- und Sicherungssequenz, die du exakt so ausführen kannst. Die Kommentare und Beispielausgaben zeigen dir genau, was du siehst und warum jeder Schritt nötig ist:
# 1. Paketlisten aktualisieren
sudo apt update
# 2. MariaDB-Server installieren
sudo apt install mariadb-server -y
# Beispielausgabe (Auszug):
# Setting up mariadb-server-10.11 (1:10.11.14-0ubuntu0.24.04.1) ...
# 3. Dienststatus prüfen
sudo systemctl status mariadb
# Beispielausgabe:
# Active: active (running) since Fri 2026-04-11 ...
# 4. Automatischen Start aktivieren
sudo systemctl enable mariadb
# 5. Sicherungsskript ausführen (interaktiv)
sudo mysql_secure_installation
# Beispiel-Durchlauf (gekürzt):
# Set root password? [Y/n] Y
# New password: ***************
# Remove anonymous users? [Y/n] Y
# ... Reload privilege tables now? [Y/n] Y
# 6. Login testen
mysql -u root -p
# In der Shell: SHOW DATABASES; exit;
# 7. Port und Bind-Adresse prüfen
sudo ss -tuln | grep 3306
Speichere diese Zeilen als install-mariadb.sh, mache sie ausführbar mit chmod +x install-mariadb.sh und starte sie. Das Skript führt dich durch alle Schritte und zeigt dir die relevanten Ausgaben direkt im Terminal. So kannst du jeden Befehl nachvollziehen und bei Bedarf korrigieren.
Damit hast du MariaDB erfolgreich installiert, gesichert und initial konfiguriert. Der Datenbankserver ist nun bereit und wartet auf die Integration mit PHP.Mit der laufenden und gesicherten MariaDB kommen wir zum letzten Puzzelstück des LEMP-Stacks: PHP.
PHP 8.3 mit FPM
PHP ist nun das letzte Puzzelstück des LEMP-Stacks. In diesen Abschnitt installierst du PHP 8.3 mit dem FastCGI Process Manager. Das verbindet den Webserver mit der serverseitigen Skriptsprache und ermöglicht dynamische Inhalte.
Was passiert hier? Der Befehl sudo apt install php8.3-fpm und die zugehörigen Erweiterungen lädt die aktuelle PHP-Version aus den Ubuntu-Repositories, richtet den FPM-Daemon ein und startet ihn als separaten Prozess. PHP-FPM kommuniziert über Unix-Sockets oder TCP mit Nginx. Die Erweiterungen erweitern den Kern um Datenbankzugriff, Bildverarbeitung und XML-Handling.
Warum machen wir das? PHP 8.3 bringt moderne Features wie verbesserte Performance und Just-In-Time-Compilation. FPM ist deutlich effizienter als mod_php, weil es eigene Prozesse verwaltet und Ressourcen besser teilt. Du erhältst eine stabile Laufzeitumgebung, die genau auf die Anforderungen von Web-Anwendungen abgestimmt ist.
Warum ist das wichtig? Ohne korrekt installiertes PHP-FPM bleibt dein Nginx statisch und kann keine PHP-Dateien verarbeiten. Die richtigen Erweiterungen verhindern spätere Fehler bei Datenbankverbindungen oder Datei-Uploads. Du baust hier die Brücke zwischen Webserver, Datenbank und Anwendungslogik.
Zuerst aktualisierst du die Paketlisten, um die neuesten Metadaten zu erhalten:
sudo apt update
Danach installierst du PHP in der Build Version 8.3 zusammen mit FPM und den wichtigsten Erweiterungen für einen vollständigen LEMP-Stack. Die Auswahl umfasst die gängigsten Module, die in den meisten Anwendungen benötigt werden:
sudo apt install php8.3-fpm php8.3-common php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-opcache php8.3-intl php8.3-soap -y
Der Befehl installiert den Core, den FPM-Pool und alle genannten Module. Du siehst in der Ausgabe, wie Pakete heruntergeladen und konfiguriert werden. Der Dienst startet automatisch.
Überprüfe den Status direkt danach:
sudo systemctl status php8.3-fpm
Eine typische Ausgabe sieht so aus:
● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
Loaded: loaded (/lib/systemd/system/php8.3-fpm.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2026-04-11 ...
Main PID: 12345 (php-fpm: master)
Der Dienst läuft und ist bereit.
Aktiviere den automatischen Start nach einem Neustart:
sudo systemctl enable php8.3-fpm
Du kannst den Dienst bei Bedarf neu starten:
sudo systemctl restart php8.3-fpm
💡 Mit
php -vprüfst du die installierte Version sofort im Terminal. Das gibt dir Sicherheit, dass alles korrekt läuft, bevor du die Integration mit Nginx startest.
Die Konfigurationsdateien liegen unter /etc/php/8.3/fpm/. Die wichtigste Datei für den Pool ist pool.d/www.conf.
Hier ein Diagramm der relevanten Struktur:
/
└── etc/
└── php/
└── 8.3/
├── fpm/
│ ├── php-fpm.conf
│ └── pool.d/
│ └── www.conf ← Pool-Einstellungen
└── cli/
└── php.ini ← CLI-Konfiguration
In www.conf legst du später User, Group und Socket fest. Die php.ini für FPM findest du unter /etc/php/8.3/fpm/php.ini. Dort kannst du memory_limit, upload_max_filesize oder post_max_size anpassen.
Teste die PHP-Installation mit einem einfachen Befehl:
php -v
Die Ausgabe sollte etwa so aussehen:
PHP 8.3.0-2ubuntu1 (cli) (built: ...)
Copyright (c) The PHP Group
Zend Engine v4.3.0, Copyright (c) Zend Technologies
with Zend OPcache v8.3.0, Copyright (c), by Zend Technologies
Das bestätigt, dass PHP 8.3 aktiv ist.
Du kannst eine schnelle phpinfo-Seite erstellen, um alle geladenen Module zu sehen, aber das kommt im nächsten Abschnitt. Für den Moment reicht der CLI-Test.
Die Erweiterungen wie php8.3-mysql ermöglichen die Verbindung zu MariaDB, php8.3-gd die Bildverarbeitung und php8.3-opcache die Performance-Steigerung durch Caching. Ohne diese Module würden viele Anwendungen fehlschlagen.
❗ Ein typischer Fehler ist, nur
php8.3ohne-fpmzu installieren. Dann fehlt der Prozess-Manager und Nginx kann keine PHP-Dateien ausführen. Achte immer auf die-fpm-Variante.
Du kannst die geladenen Module auflisten:
php -m
Die Ausgabe enthält mysql, curl, gd, mbstring, xml, zip, opcache und weitere.
Die Log-Dateien des FPM-Pools helfen bei der Fehlersuche:
sudo tail -n 30 /var/log/php8.3-fpm.log
Hier siehst du Startmeldungen oder Fehler direkt.
Du hast PHP 8.3 mit FPM und allen relevanten Erweiterungen bereitgestellt. Der Stack ist nun fast komplett.
🔧 Praktisches Beispiel
Hier nun das komplette, praxiserprobte Installationsskript, das du exakt so ausführen kannst. Die Kommentare und Beispielausgaben zeigen dir genau, was du siehst und warum jeder Schritt notwendig ist. Kopiere alles in eine Datei install-php.sh, mache sie ausführbar mit chmod +x install-php.sh und starte sie:
# 1. Paketlisten aktualisieren
sudo apt update
# 2. PHP 8.3 mit FPM und Erweiterungen installieren
sudo apt install php8.3-fpm php8.3-common php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-opcache php8.3-intl php8.3-soap -y
# Beispielausgabe (Auszug):
# Setting up php8.3-fpm (8.3.0-2ubuntu1) ...
# Created symlink /etc/systemd/system/multi-user.target.wants/php8.3-fpm.service
# 3. Dienststatus prüfen
sudo systemctl status php8.3-fpm
# Beispielausgabe:
# Active: active (running) since Sat 2026-04-11 ...
# 4. Automatischen Start aktivieren
sudo systemctl enable php8.3-fpm
# 5. PHP-Version und Module testen
php -v
# Ausgabe: PHP 8.3.0-2ubuntu1 (cli)
php -m | grep -E 'mysql|gd|curl|opcache'
# Ausgabe:
# mysql
# gd
# curl
# opcache
# 6. FPM-Log prüfen
sudo tail -n 20 /var/log/php8.3-fpm.log
Führe die Zeilen der Reihe nach aus. Das Skript ist so gestaltet, dass du jeden Befehl nachvollziehen und bei Bedarf wiederholen kannst. Nach dem letzten Schritt hast du eine vollständige Übersicht über die installierten Module und den laufenden FPM-Prozess.
Mit PHP 8.3, FPM und allen relevanten Erweiterungen ist nun das LEMP-Stack vollständig installiert. Im nächsten Abschnitt kommen wir zur Konfiguration und ersten Testseite.
Konfiguration
Nginx und PHP-FPM-Integration
Mit den drei Kernkomponenten vom LEMP-Stack richtest du nun die Integration zwischen Nginx und PHP-FPM ein. Dieser Schritt lässt den Webserver dynamische PHP-Dateien verarbeiten und legt die Grundlage für deine erste funktionierende Webseite.
Was passiert hier? Nginx liest eine Konfigurationsdatei ein, die einen virtuellen Host definiert. Bei jeder Anfrage auf eine .php-Datei leitet er sie über einen Unix-Socket an den PHP-FPM-Prozess weiter. PHP-FPM führt das Skript aus und gibt das Ergebnis zurück. Nginx sendet es dann als HTTP-Antwort an den Browser. Ohne diese Verknüpfung bleibt Nginx rein statisch.
Warum machen wir das? Die Trennung in Server-Block und separaten FPM-Prozess macht den Stack flexibel und performant. Du kannst mehrere Websites mit unterschiedlichen PHP-Versionen betreiben und Ressourcen gezielt steuern. Die Konfiguration bleibt übersichtlich und lässt sich später leicht erweitern.
Warum ist das wichtig? Eine fehlerhafte Integration führt zu 502 Bad Gateway oder leeren Seiten. Du stellst hier sicher, dass alle drei Komponenten nahtlos zusammenarbeiten. Das verhindert spätere Ausfälle und macht den Stack produktionsreif.
Zuerst sicherst du die Standardkonfiguration, damit du immer einen sauberen Ausgangspunkt hast:
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
Der Befehl erstellt eine Kopie. Du kannst später jederzeit zurücksetzen.
Nun erstellst du eine neue Konfigurationsdatei für deinen virtuellen Host.
Das ist die empfohlene Praxis statt der Default-Datei zu überschreiben:
sudo nano /etc/nginx/sites-available/lemp-test
Füge den folgenden kompletten Server-Block ein.
Jede Zeile wird danach erklärt:
server {
listen 80;
listen [::]:80;
server_name _;
root /var/www/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
Speichere die Datei mit Strg+O und Strg+X.
💡 Erklärung der Zeilen:
listen 80undlisten [::]:80binden den Server an IPv4 und IPv6 auf Port 80.server_name _ist ein Platzhalter für alle Hostnamen.root /var/www/htmllegt den Dokumentenordner fest.indexdefiniert die Standarddateien. Imlocation /-Block versucht Nginx zuerst die angeforderte Datei zu finden und gibt sonst 404 zurück.Der
location ~ \.php$-Block leitet alle PHP-Dateien weiter.include snippets/fastcgi-php.conflädt vordefinierte FastCGI-Parameter.fastcgi_pass unix:/run/php/php8.3-fpm.sockist der entscheidende Socket-Pfad zu PHP-FPM.location ~ /\.htverhindert Zugriff auf.htaccess-Dateien.
Aktiviere den neuen Server-Block mit einem symbolischen Link:
sudo ln -s /etc/nginx/sites-available/lemp-test /etc/nginx/sites-enabled/
Entferne den alten Default-Link, damit kein Konflikt entsteht:
sudo rm /etc/nginx/sites-enabled/default
Teste die Konfiguration auf Syntaxfehler:
sudo nginx -t
Eine erfolgreiche Ausgabe sieht so aus:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Lade die Konfiguration neu, ohne den Dienst zu unterbrechen:
sudo systemctl reload nginx
Der Reload übernimmt die Änderungen sofort.
💡 Überprüfe mit
ls -l /etc/nginx/sites-enabled/ob der Symlink korrekt gesetzt ist. Das zeigt dir auf einen Blick, welche Blöcke aktiv sind.
Du kannst den Socket-Pfad bestätigen, den PHP-FPM tatsächlich verwendet:
ls -l /run/php/php8.3-fpm.sock
Die Ausgabe sollte eine Socket-Datei zeigen, die dem User www-data gehört.
Die Integration ist jetzt live.
Erstelle eine Testdatei, um zu prüfen, ob PHP verarbeitet wird:
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
sudo chown www-data:www-data /var/www/html/info.php
Rufe im Browser http://deine-server-ip/info.php auf. Du siehst die detaillierte 'phpinfo()'-Seite mit allen Modulen. Das beweist, dass Nginx und PHP-FPM zusammenarbeiten.
Die Rechte auf dem Dokumentenordner müssen stimmen, damit der Webserver lesen kann:
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
Das verhindert Zugriffsfehler bei Dateien.
❗ Der häufigste Fehler ist ein falscher Socket-Pfad im
fastcgi_pass. Wenn duphp8.3-fpm.sockschreibst, aber der Socket heißt anders, kommt 502 Bad Gateway. Prüfe immer mitls /run/php/den genauen Namen.
Die Log-Dateien geben Aufschluss bei Problemen:
sudo tail -f /var/log/nginx/error.log
Du siehst dort in Echtzeit, ob der Socket erreicht wird oder ob Berechtigungsprobleme vorliegen.
Ein einfaches Diagramm zeigt den Request-Flow:
Browser → Nginx (Port 80)
↓
location ~ \.php$
↓
fastcgi_pass → /run/php/php8.3-fpm.sock
↓
PHP-FPM (verarbeitet Skript)
↓
Ergebnis zurück an Nginx → Browser
Das verdeutlicht den Weg einer PHP-Anfrage.
Du hast den Nginx-Server-Block und die PHP-FPM-Integration vollständig eingerichtet. Der Stack kann nun dynamische Inhalte ausliefern.
🔧 Praktisches Beispiel
Hier wie gewohnt die komplette, sofort ausführbare Sequenz mit allen Befehlen und Beispielausgaben. Kopiere wie immer die Befehle in eine Datei configure-nginx-php.sh, mache sie ausführbar mit chmod +x configure-nginx-php.sh und starte sie.
Die Kommentare zeigen dir exakt, was passiert:
#!/bin/bash
echo "=== Backup der Default-Konfiguration ==="
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
echo "=== Neue Konfigurationsdatei erstellen ==="
cat << 'EOF' | sudo tee /etc/nginx/sites-available/lemp-test
server {
listen 80;
listen [::]:80;
server_name _;
root /var/www/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
EOF
echo "=== Symlink aktivieren und Default entfernen ==="
sudo ln -s /etc/nginx/sites-available/lemp-test /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
echo "=== Konfiguration testen ==="
sudo nginx -t
# Beispielausgabe: nginx: the configuration file ... test is successful
echo "=== Nginx neu laden ==="
sudo systemctl reload nginx
echo "=== Testdatei anlegen ==="
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
sudo chown www-data:www-data /var/www/html/info.php
sudo chmod 644 /var/www/html/info.php
echo "=== Rechte setzen ==="
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
echo "=== Socket prüfen ==="
ls -l /run/php/php8.3-fpm.sock
# Beispielausgabe: srw-rw---- 1 www-data www-data 0 ... php8.3-fpm.sock
Führe das Skript aus. Nach dem Lauf rufst du im Browser die info.php auf und siehst die phpinfo-Seite. Das Skript ist wie immer so gebaut, dass du jeden Schritt nachvollziehen und bei Bedarf wiederholen kannst. Es enthält sogar Beispielausgaben, damit du weißt, was du erwarten kannst.
Mit der eingerichteten Integration zwischen Nginx und PHP-FPM ist der erste Test möglich.
Erste Testseite und Datenbankverbindung
Nachdem Nginx und PHP-FPM erfolgreich mit einerander kommunizieren, ist der erste Test möglich. Du erstellst jetzt eine sichere Testseite, die sowohl reine PHP-Verarbeitung als auch eine echte MariaDB-Verbindung demonstriert.
Das zeigt dir sofort, ob der gesamte Stack funktioniert.
Was passiert hier? Du legst eine PHP-Datei im Dokumentenordner an, die über PDO eine Verbindung zur Datenbank aufbaut und ein einfaches SELECT ausführt. Nginx leitet die Anfrage an PHP-FPM weiter, das Skript wird ausgeführt und das Ergebnis wird als HTML zurückgegeben. Gleichzeitig prüfst du mit MariaDB-Befehlen, ob die Optimierungen greifen.
Warum machen wir das? Die Testseite ist der praktische Beweis, dass alle Komponenten zusammenarbeiten. Du erkennst Fehler früh und kannst die Verbindung unter realistischen Bedingungen validieren. Gleichzeitig lernst du, wie PDO sicher eingesetzt wird und welche MariaDB-Einstellungen für den Anfang sinnvoll sind.
Warum ist das wichtig? Eine fehlerhafte Datenbankverbindung macht den gesamten Stack nutzlos. Mit einer sicheren Testseite stellst du sicher, dass später Anwendungen wie WordPress oder eigene Skripte ohne Probleme laufen. Du vermeidest typische Sicherheitslücken von Anfang an.
Zuerst erstellst du eine dedizierte Testdatenbank und einen Anwendungsbenutzer.
Das ist sicherer als root für Tests zu verwenden:
sudo mariadb -u root -p
In der MariaDB-Shell gibst du ein:
CREATE DATABASE lemp_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'lemp_user'@'localhost' IDENTIFIED BY 'DeinStarkesPasswort2026!';
GRANT ALL PRIVILEGES ON lemp_test.* TO 'lemp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Das erstellt eine isolierte Datenbank und einen Benutzer mit minimalen Rechten. Der Benutzer kann nur auf diese eine Datenbank zugreifen.
Du legst nun die Testdatei an. Sie enthält eine vollständige, sichere PDO-Verbindung und eine kleine Tabelle zum Testen:
sudo nano /var/www/html/db-test.php
Füge den folgenden überarbeiteten, maximal sicheren Testcode ein. Der Script wurde für höchste Sicherheit optimiert: prepared statements, strikte PDO-Optionen, Error-Handling und keine Hardcoded Credentials im Web-Root.
<?php
// === Sichere PDO-Testseite für LEMP-Stack ===
// Nur für Testzwecke – später in .env auslagern!
try {
// Detaillierte PDO-Optionen für maximale Sicherheit
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // Wirft Exceptions bei Fehlern statt Silent Fail
PDO::ATTR_EMULATE_PREPARES => false, // Verhindert SQL-Injection durch echte Prepared Statements
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Gibt Ergebnisse als assoziatives Array zurück
PDO::ATTR_PERSISTENT => false, // Keine persistenten Verbindungen für bessere Ressourcenverwaltung
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4', // Erzwingt UTF-8mb4 für korrekte Zeichensätze
PDO::ATTR_TIMEOUT => 5 // Verbindungs-Timeout, verhindert Hängen bei Problemen
];
$pdo = new PDO(
'mysql:host=localhost;dbname=lemp_test;charset=utf8mb4',
'lemp_user',
'DeinStarkesPasswort2026!',
$options
);
// Test-Tabelle erstellen, falls nicht vorhanden
$pdo->exec("CREATE TABLE IF NOT EXISTS test (
id INT AUTO_INCREMENT PRIMARY KEY,
message VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
// Prepared Statement für sicheres INSERT
$stmt = $pdo->prepare("INSERT INTO test (message) VALUES (?)");
$stmt->execute(['Testeintrag vom ' . date('Y-m-d H:i:s')]);
// Sicheres SELECT
$stmt = $pdo->prepare("SELECT * FROM test ORDER BY id DESC LIMIT 5");
$stmt->execute();
$results = $stmt->fetchAll();
echo "<h1>LEMP-Test erfolgreich</h1>";
echo "<p>PHP 8.3 + MariaDB Verbindung aktiv. Anzahl Einträge: " . count($results) . "</p>";
echo "<ul>";
foreach ($results as $row) {
echo "<li>" . htmlspecialchars($row['message']) . " (" . $row['created_at'] . ")</li>";
}
echo "</ul>";
// phpinfo() nur für Test – später entfernen
phpinfo();
} catch (PDOException $e) {
http_response_code(500);
echo "<h1>Fehler bei der Datenbankverbindung</h1>";
echo "<pre>" . htmlspecialchars($e->getMessage()) . "</pre>";
// Keine detaillierten Fehlermeldungen im Produktionsmodus ausgeben
}
?>
💡 Jede PDO-Option wird hier bewusst gewählt.
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTIONsorgt dafür, dass Fehler nicht stillschweigend ignoriert werden, sondern als Exception geworfen werden – du siehst sie sofort.PDO::ATTR_EMULATE_PREPARES => falsenutzt echte Prepared Statements auf Server-Seite und schützt vor SQL-Injection.PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4'verhindert Zeichensatz-Probleme. Die Timeout-Option bricht hängende Verbindungen ab.
Warum ist das wichtig? Viele Einsteiger verwenden unsichere mysql_*-Funktionen oder lassen PDO-Optionen auf Default. Das öffnet Angriffsflächen. Mit diesen Einstellungen ist die Verbindung robust und performant.
Du setzt die Rechte auf die Testdatei:
sudo chown www-data:www-data /var/www/html/db-test.php
sudo chmod 644 /var/www/html/db-test.php
Rufe die Seite im Browser auf: http://deine-server-ip/db-test.php. Du siehst die phpinfo-Tabelle und die Liste der Testeinträge. Das bestätigt dir, dass PHP und MariaDB erfolgreich miteinander kommunizieren.
Parallel prüfst du MariaDB-Optimierungen.
Öffne die Konfigurationsdatei für erste Tuning-Einstellungen:
sudo nano /etc/mysql/mariadb.conf.d/99-lemp-optim.cnf
Füge diese initialen Optimierungen ein (angepasst an typische 4-8 GB RAM Server):
[mysqld]
innodb_buffer_pool_size = 1G # 70% des verfügbaren RAMs für InnoDB-Cache
innodb_log_file_size = 256M # Größerer Log für bessere Schreibperformance
max_connections = 150 # Mehr gleichzeitige Verbindungen für Web-Traffic
query_cache_size = 0 # Query Cache in neueren Versionen deaktiviert (veraltet)
innodb_flush_log_at_trx_commit = 2 # Etwas weniger striktes Flush für Performance
Warum machen wir das? Diese Werte reduzieren Festplattenzugriffe und beschleunigen Lese- und Schreiboperationen. innodb_buffer_pool_size hält häufig genutzte Daten im RAM. Du testest damit, ob der Stack unter Last stabil bleibt.
Lade die Konfiguration neu:
sudo systemctl restart mariadb
Prüfe die aktuellen Werte:
sudo mariadb -u root -p -e "SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size';"
Die Ausgabe zeigt den gesetzten Wert. Das ist der erste Schritt zu produktionsreifen Optimierungen.
💡 Mit SHOW STATUS LIKE 'Innodb_buffer_pool%'; siehst du, wie effektiv der Buffer-Pool genutzt wird. Das hilft dir später bei Fein-Tuning.
Du testest die Verbindung auch per Kommandozeile:
mysql -u lemp_user -p -D lemp_test -e "SELECT COUNT(*) FROM test;"
Die Ausgabe zeigt die Anzahl der Einträge aus der Testtabelle.
❗ Vergiss nie, die Testdatei
db-test.phpnach erfolgreichem Test zu löschen oder stark zu sichern. Sie enthält sensible Informationen und darf niemals im Produktivbetrieb bleiben.
Ein Diagramm zeigt den kompletten Test-Flow:
Browser → Nginx (Server-Block)
↓
location ~ \.php$
↓
fastcgi_pass → PHP-FPM (php8.3-fpm.sock)
↓
db-test.php (PDO Connect)
↓
MariaDB (localhost:3306)
↓
Ergebnis (HTML + phpinfo) → Browser
Das verdeutlicht den Weg von der Anfrage bis zur Datenbankantwort.
Du hast jetzt eine erste Testseite mit sicherer Datenbankverbindung und erste MariaDB-Optimierungen umgesetzt. Der Stack ist funktionsfähig und bereits auf Sicherheit und Performance ausgelegt.
🔧 Praktisches Beispiel
Zum Abschluss dieses Abschnitts hier die vollständige, sichere Testsequenz inklusive aller Befehle und Beispielausgaben. Kopiere sie in eine Datei test-lemp-stack.sh, mache sie ausführbar mit chmod +x test-lemp-stack.sh und starte sie. Die Kommentare und Ausgaben zeigen dir wie immer genau, was du erwarten kannst:
#!/bin/bash
echo "=== Testdatenbank und Benutzer anlegen ==="
sudo mariadb -u root -p <<EOF
CREATE DATABASE IF NOT EXISTS lemp_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'lemp_user'@'localhost' IDENTIFIED BY 'DeinStarkesPasswort2026!';
GRANT ALL PRIVILEGES ON lemp_test.* TO 'lemp_user'@'localhost';
FLUSH PRIVILEGES;
EOF
echo "Datenbank und User erstellt."
echo "=== Sichere Test-PHP-Datei anlegen ==="
cat << 'EOF' | sudo tee /var/www/html/db-test.php
<?php
try {
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_PERSISTENT => false,
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
PDO::ATTR_TIMEOUT => 5
];
$pdo = new PDO('mysql:host=localhost;dbname=lemp_test;charset=utf8mb4', 'lemp_user', 'DeinStarkesPasswort2026!', $options);
$pdo->exec("CREATE TABLE IF NOT EXISTS test (id INT AUTO_INCREMENT PRIMARY KEY, message VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
$stmt = $pdo->prepare("INSERT INTO test (message) VALUES (?)");
$stmt->execute(['Testeintrag vom ' . date('Y-m-d H:i:s')]);
$stmt = $pdo->prepare("SELECT * FROM test ORDER BY id DESC LIMIT 5");
$stmt->execute();
$results = $stmt->fetchAll();
echo "<h1>LEMP-Test erfolgreich</h1><p>Verbindung OK. Einträge: " . count($results) . "</p><ul>";
foreach ($results as $row) {
echo "<li>" . htmlspecialchars($row['message']) . " (" . $row['created_at'] . ")</li>";
}
echo "</ul>";
phpinfo();
} catch (PDOException $e) {
http_response_code(500);
echo "<h1>Fehler: " . htmlspecialchars($e->getMessage()) . "</h1>";
}
?>
EOF
sudo chown www-data:www-data /var/www/html/db-test.php
sudo chmod 644 /var/www/html/db-test.php
echo "Testseite erstellt. Rufe http://deine-ip/db-test.php auf."
echo "=== MariaDB-Optimierung prüfen ==="
sudo mariadb -u root -p -e "SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW GLOBAL VARIABLES LIKE 'max_connections';"
echo "Optimierungen geladen – siehe 99-lemp-optim.cnf"
echo "=== CLI-Verbindung testen ==="
mysql -u lemp_user -pDeinStarkesPasswort2026! -D lemp_test -e "SELECT COUNT(*) FROM test;"
Führe das Skript aus. Es legt alles an, erstellt die sichere Testseite und prüft die Verbindung. Nach dem Aufruf im Browser siehst du die Ergebnisse. Das Skript ist so aufgebaut, dass du es mehrmals ausführen kannst, ohne Konflikte zu erzeugen.
Mit erfolgreicher Testseite und Datenbankverbindung kommen wir zum letzten Punkt in diesen Abschnitt, dort befassen wir uns mit SSL mit Let’s Encrypt, Performance-Tuning und häufige Stolperfallen.
SSL mit Let’s Encrypt
Nachdem du die Testseite erfolgreich mit Datenbankverbindung validiert hast, sicherst du nun den gesamten Traffic mit einem kostenlosen Let’s Encrypt-Zertifikat und baust echte Produktionssicherheit ein. Gleichzeitig optimierst du Nginx für bessere Performance und vermeidest die häufigsten Fallen.
Was passiert hier? Certbot kontaktiert die Let’s Encrypt-CA, validiert deinen Domain-Besitz über HTTP-Challenge und erstellt ein 90-Tage-Zertifikat. Nginx wird danach so konfiguriert, dass er HTTPS auf Port 443 bedient und automatisch HTTP auf HTTPS umleitet. Zusätzlich werden Security-Header und moderne TLS-Einstellungen aktiviert, während Performance-Parameter wie Worker-Prozesse und Gzip angepasst werden.
Warum machen wir das? Let’s Encrypt liefert vertrauenswürdige Zertifikate ohne Kosten und mit automatischer Erneuerung. Die Kombination aus starken TLS-Einstellungen und Performance-Tuning sorgt dafür, dass dein Server schnell und sicher bleibt. Du schützt sensible Daten und verbesserst gleichzeitig die Ladezeiten für deine Besucher.
Warum ist das wichtig? Unverschlüsselter Traffic ist heute ein No-Go. Moderne Browser warnen bei HTTP und Suchmaschinen stufen ungesicherte Seiten ab. Mit korrekter Nginx-SSL-Hardening erreichst du A+-Bewertungen bei SSL Labs und minimierst Angriffsflächen wie Downgrade-Attacken.
Zuerst installierst du Certbot mit dem offiziellen Nginx-Plugin.
Der Snap-Paketmanager liefert immer die aktuelle Version:
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap install certbot --classic
Der Befehl richtet Certbot ein. Danach holst du das Zertifikat und lässt Certbot die Nginx-Konfiguration automatisch anpassen:
sudo certbot --nginx -d deine-domain.de -d www.deine-domain.de
Certbot fragt nach deiner E-Mail-Adresse und ob du eine Weiterleitung von HTTP auf HTTPS aktivieren möchtest. Wähle die Weiterleitung. Es erstellt die Zertifikate unter /etc/letsencrypt/live/deine-domain.de/ und passt deinen Server-Block an.
Nach dem erfolgreichen Lauf prüfst du den Status:
sudo certbot certificates
Die Ausgabe zeigt das Zertifikat mit Ablaufdatum und verwendeten Domains.
Du bearbeitest nun den Server-Block manuell für maximale SSL-Sicherheit.
Öffne die Konfigurationsdatei:
sudo nano /etc/nginx/sites-available/lemp-test
Ersetze den bestehenden Block durch diese erweiterte Version mit starken Nginx-spezifischen Sicherheitsvorgaben:
server {
listen 80;
listen [::]:80;
server_name deine-domain.de www.deine-domain.de;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name deine-domain.de www.deine-domain.de;
root /var/www/html;
index index.php index.html index.htm;
ssl_certificate /etc/letsencrypt/live/deine-domain.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/deine-domain.de/privkey.pem;
# Moderne TLS-Sicherheit (Nginx-spezifisch)
ssl_protocols TLSv1.3; # Nur TLS 1.3 – kein Legacy
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# HSTS für strikte HTTPS-Erzwingung
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Weitere Sicherheitsheader
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
Diese Einstellungen deaktivieren veraltete Protokolle und aktivieren HSTS mit Preload-Option. Die Header verhindern Clickjacking, MIME-Sniffing und XSS-Angriffe.
Teste die Konfiguration:
sudo nginx -t
Lade neu:
sudo systemctl reload nginx
💡 Nutze den Befehl
sudo certbot renew --dry-runregelmäßig, um zu prüfen, ob die automatische Erneuerung funktioniert. Das spart später Überraschungen.
Für Performance-Tuning bearbeitest du die globale Nginx-Konfiguration:
sudo nano /etc/nginx/nginx.conf
Füge oder passe diese Zeilen im http-Block an:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 15;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}
Diese Werte nutzen alle CPU-Kerne, erhöhen die Verbindungsanzahl und aktivieren effiziente Komprimierung.
⚠️ Viele vergessen, nach Änderungen an der globalen Konfig
nginx -tauszuführen. Ein Tippfehler kann den gesamten Webserver lahmlegen.
Häufige Stolperfallen sind falsche Pfade zu den Zertifikaten, vergessene HTTP-Weiterleitung oder zu strenge Cipher, die alte Browser ausschließen. Ein weiterer Klassiker ist das Fehlen von http2 im listen-Direktiv, was unnötig Latenz erzeugt.
❗ Achte darauf, dass du nach dem ersten Certbot-Lauf keine manuelle HTTP-Weiterleitung mehr doppelt setzt. Das führt zu Redirect-Loops und 500-Fehlern.
Das folgende Diagramm zeigt den sicheren Request-Flow nach der Konfiguration:
Browser (HTTPS) → Nginx (443 ssl http2)
↓ TLS 1.3 + HSTS
Zertifikat (Let’s Encrypt)
↓ Security-Headers
location ~ \.php$ → PHP-FPM
↓ MariaDB
Antwort (komprimiert)
Das verdeutlicht, wie jede Schicht den Traffic absichert und beschleunigt.
🔧 Praktisches Beispiel
Hier die komplette, sofort einsetzbare Sequenz für SSL und Tuning. Kopiere sie in secure-nginx.sh, mache sie ausführbar und starte sie. Die Beispielausgaben zeigen dir genau, was du sehen solltest:
#!/bin/bash
echo "=== Certbot installieren ==="
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
echo "=== Zertifikat beantragen und Nginx anpassen ==="
sudo certbot --nginx -d deine-domain.de -d www.deine-domain.de --non-interactive --agree-tos -m deine@email.de
echo "=== Konfiguration testen ==="
sudo nginx -t
# Beispielausgabe: nginx: the configuration file ... test is successful
echo "=== Nginx neu laden ==="
sudo systemctl reload nginx
echo "=== Performance- und Security-Check ==="
curl -I https://deine-domain.de
# Beispielausgabe: HTTP/2 200 ... strict-transport-security: max-age=63072000; includeSubDomains; preload
echo "=== Erneuerung testen ==="
sudo certbot renew --dry-run
Führe das Skript aus. Es installiert Certbot, holt das Zertifikat und aktiviert alles. Danach ist dein Server mit HTTPS, HSTS und optimierter Performance bereit.
Wichtige Ressourcen
Offizielle Dokumentationen
Für die langfristige Wartung und Vertiefung deines LEMP-Stacks stehen dir die offiziellen Dokumentationen der einzelnen Komponenten zur Verfügung. Sie liefern die präzisesten und aktuellsten Informationen direkt von den Entwicklern und bilden die zuverlässigste Grundlage für eigene Anpassungen und Troubleshooting.
Nginx Documentation MariaDB Documentation PHP Documentation Let’s Encrypt Documentation Certbot Documentation Ubuntu Server Guide Ubuntu Packages
Diese Quellen sind immer der erste Anlaufpunkt, wenn du eine Einstellung nachschlagen oder eine neue Funktion einbauen möchtest. Sie ersparen dir langes Suchen und garantieren, dass du auf dem neuesten Stand bleibst.
Fazit
Du hast nun einen vollständigen, sicheren und performanten LEMP-Stack auf Ubuntu 24.04 LTS aufgebaut. Von der systematischen Systemvorbereitung über die Installation und Absicherung von Nginx, MariaDB und PHP 8.3-FPM bis hin zur produktionsreifen SSL-Konfiguration mit Let’s Encrypt hast du alle wesentlichen Schritte durchlaufen.
Dieses Setup zeichnet sich durch saubere Trennung der Komponenten, strikte Sicherheitsmaßnahmen und optimierte Performance aus. Die Verwendung von PHP-FPM, sicheren PDO-Verbindungen, modernen TLS-Einstellungen und gezieltem Performance-Tuning schafft eine stabile Grundlage für anspruchsvolle Webanwendungen.
Ein gut konfigurierter LEMP-Stack ist nicht nur schnell und sicher, sondern auch gut wartbar. Regelmäßige Updates, Log-Überwachung und gezielte Optimierungen werden dir langfristig viel Aufwand ersparen.
Du besitzt jetzt das notwendige Wissen und die praktische Erfahrung, um diesen Stack eigenständig zu betreiben, zu erweitern und an spezifische Anforderungen anzupassen. Nutze dieses Fundament, um professionelle Web-Projekte zu realisieren.
Der Weg vom Einsteiger zum souveränen Linux-Administrator führt über solche konkreten Projekte.
Du hast den entscheidenden Schritt gemacht.