---
id: 2024-10-13-ubuntu-24-04-lemp-stack-installieren
slug: ubuntu-24-04-lemp-stack-installieren
title: "LEMP-Stack auf Ubuntu 24.04 LTS installieren: Der vollständige Praxisleitfaden"
excerpt: "Vom frischen Ubuntu 24.04 bis zum produktionsreifen LEMP-Stack mit Nginx, MariaDB, PHP 8.3-FPM und Let’s Encrypt: Schritt-für-Schritt-Anleitung für sichere und performante Webserver."
date: "2024-10-13T09:00:00+02:00"
updated: "2026-04-11T09:00:00+02:00"
author:
  name: "Sebastian Palencsár"
  handle: "spalencsar"
category: ["serverumgebungen"]
tags: ["lempstack", "ubuntu2404", "nginx", "mariadb", "php8", "linuxadmin"]
reading_time: 27
toc: true
---

Der LEMP-Stack repräsentiert eine der bewährtesten Architekturen für den Betrieb dynamischer Web-Anwendungen unter Linux. [Linux](/de/tag/linux){.badge-link-text} bildet die stabile Grundlage, [Nginx](https://www.nginx.com/){.badge-link-text} den effizienten Webserver, [MariaDB](https://mariadb.org/){.badge-link-text} den relationalen Datenbankserver und [PHP](https://www.php.net/){.badge-link-text} 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](https://ubuntu.com/download/server){.badge-link-text} 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. 

<blockquote class="infobox infobox--info">
💡 Die LTS-Version von Ubuntu bietet langfristigen Support und eine konsistente Paketbasis, die für produktive Umgebungen unerlässlich ist und langfristige Planung erleichtert. 
</blockquote>

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. 

<blockquote class="infobox infobox--practice">
❗ 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. 
</blockquote>

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. 

<blockquote class="infobox infobox--warn">
⚠️ 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. 
</blockquote>

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.

<span class="nb-accent">Was passiert hier genau?</span> 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.

<span class="nb-accent">Warum machen wir das?</span> 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. 

<span class="nb-accent">Warum ist das wichtig?</span> 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:

```bash
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:

```bash
sudo apt upgrade -y
```

<span class="nb-accent">Was passiert hier?</span> 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. 


<blockquote class="infobox infobox--info">
💡 Führe `sudo apt update` regelmäßig vor jeder größeren Installation aus – das spart dir später Stunden bei der Fehlersuche durch Versionskonflikte.
</blockquote>

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:

```bash
sudo apt autoremove -y
```

Und mit `autoclean` löschst du heruntergeladene Paketdateien aus dem Cache, die nicht mehr aktuell sind:

```bash
sudo apt autoclean
```

Diese beiden Befehle halten den Speicherplatz frei und verhindern, dass der Cache über die Zeit unnötig wächst.

🔧 <span class="nb-accent">Praktisches Beispiel</span>  

Hier siehst du die komplette, praxisnahe Sequenz, die du genau so in dein Terminal kopieren kannst. 

**Die Kommentare erklären jede Zeile:**

```bash
# 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.

<blockquote class="infobox infobox--practice">
❗ 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.
</blockquote>

Zusätzlich kannst du den Status deiner Repositories mit einem einzelnen Befehl überprüfen:

```bash
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](https://wiki.ubuntuusers.de/UFW/){.badge-link-text} mit – die Uncomplicated Firewall. Sie ist ein benutzerfreundliches Frontend für [nftables](https://wiki.ubuntuusers.de/nftables/){.badge-link-text} und schützt dein System vor unerwünschten Zugriffen, ohne dass du komplexe Regeln von Hand schreiben musst.

<span class="nb-accent">Was passiert hier?</span> 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.

<span class="nb-accent">Warum machen wir das?</span> 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.

<span class="nb-accent">Warum ist das wichtig?</span> 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:

```bash
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:

```bash
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:

```bash
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.

<blockquote class="infobox infobox--info">
💡 Bevor du die Firewall aktivierst, prüfe mit `sudo ufw status` den aktuellen Zustand. So siehst du sofort, ob alle notwendigen Regeln vorhanden sind.
</blockquote>

Nun aktivierst du UFW. Der Befehl startet den Dienst und lädt die Regeln:

```bash
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:**

```bash
sudo ufw status verbose
```

Die Ausgabe zeigt die aktiven Regeln, die Standardrichtlinien und ob IPv6 aktiviert ist. Du siehst zum Beispiel:

```bash
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](https://fail2ban.github.io/fail2ban/){.badge-link-text}. 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.

```bash
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.

<blockquote class="infobox infobox--practice">
❗ 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.
</blockquote>

🔧 <span class="nb-accent">Praktisches Beispiel</span>
Hier die komplette, praxiserprobte Sequenz, die du exakt so ausführen kannst. 

**Die Kommentare erklären jede Zeile:**

```bash
# 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.

<span class="nb-accent">Was passiert hier?</span> 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.

<span class="nb-accent">Warum machen wir das?</span> 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.

<span class="nb-accent">Warum ist das wichtig?</span> 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:

```bash
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:**

```bash
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. 

<blockquote class="infobox infobox--info">
💡 Mit `free -h` erkennst du sofort, ob dein Server unter Speichermangel leidet. Führe den Befehl nach einem Neustart aus, damit die Werte repräsentativ sind. 
</blockquote>

Du kontrollierst nun den Hostnamen und die Netzwerkkonfiguration. Ein eindeutiger Hostname hilft später bei der Verwaltung mehrerer Server:

```bash
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:

```bash
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:

```bash
timedatectl
```

Die Ausgabe zeigt `Time zone` und `NTP service: active`. 

**Ist NTP nicht aktiv, synchronisiere mit:**

```bash
sudo timedatectl set-timezone Europe/Berlin
sudo systemctl restart systemd-timesyncd
```

Du prüfst, ob bereits ein Webserver wie [Apache](https://httpd.apache.org/){.badge-link-text} läuft. Dieser würde mit Nginx kollidieren und Port 80 blockieren:

```bash
sudo systemctl status apache2
```

**Falls der Dienst aktiv ist, deaktiviere ihn:**

```bash
sudo systemctl stop apache2
sudo systemctl disable apache2
sudo apt purge apache2 -y
```

Das entfernt Apache vollständig und verhindert Konflikte.

[AppArmor](https://wiki.ubuntuusers.de/AppArmor/){.badge-link-text}, die Sicherheitsrichtlinie von Ubuntu, sollte aktiv sein und später für die Dienste konfiguriert werden. Überprüfe den Status:

```bash
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:

```bash
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.

<blockquote class="infobox infobox--practice">
❗ Manche Administratoren überspringen den Check auf `Apache` und installieren `Nginx` direkt. Dann hört der Webserver nicht oder bindet nicht an `Port 80`. Immer zuerst bestehende Dienste prüfen und entfernen. 
</blockquote>

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:

```bash
apt list --installed | grep php
```

Falls ältere Pakete auftauchen, entferne sie mit `sudo apt purge php* -y`.

🔧 <span class="nb-accent">Praktisches Beispiel</span>

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:**

```bash
#!/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</span> als Webserver. 

Der Befehl lädt das Paket aus den offiziellen Ubuntu-Repositories und richtet den Dienst sofort ein.

```bash
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.

<span class="nb-accent">Warum machen wir das?</span> [Nginx](https://www.nginx.com/){.badge-link-text} ist leichtgewichtig, verarbeitet Anfragen asynchron und verbraucht deutlich weniger Ressourcen als Alternativen wie [Apache](https://httpd.apache.org/){.badge-link-text}. Du legst damit die Grundlage für schnelle, stabile Webseiten, die später mit [PHP](https://www.php.net/){.badge-link-text} und [MariaDB](https://mariadb.com/){.badge-link-text} zusammenarbeiten. Die Installation aus den Ubuntu-Repos garantiert Kompatibilität und einfache Updates über das Paketmanagement.

<span class="nb-accent">Warum ist das wichtig?</span> 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:

```bash
sudo apt update
```

Danach installierst du Nginx mit der `-y`-Option, damit alle Rückfragen automatisch bestätigt werden:

```bash
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:**

```bash
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:

```bash
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
```

Aktiviere den automatischen Start nach einem Neustart des Servers:

```bash
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:**

```bash
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:

```bash
curl -I http://localhost
```

Die Antwort `HTTP/1.1 200 OK` zeigt, dass alles funktioniert.

<blockquote class="infobox infobox--info">
💡 Nach der Installation schau dir die Verzeichnisstruktur an, um später gezielt zu konfigurieren.
</blockquote>

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:

```markdown
/
└── 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.

```bash
sudo nginx -t
```

Das ist ein wichtiger Sicherheits-Check, bevor du Änderungen übernimmst

<blockquote class="infobox infobox--practice">
❗ 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 80` und beende den Konflikt sofort.
</blockquote>

Du kannst den Log-Ordner einsehen, um Fehler direkt zu erkennen:

```bash
sudo tail -f /var/log/nginx/error.log
```

So beobachtest du in Echtzeit, was passiert, wenn du später Konfigurationen änderst.

🔧 <span class="nb-accent">Praktisches Beispiel</span>

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:

```bash
# 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.

<span class="nb-accent">Was passiert hier?</span> 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.

<span class="nb-accent">Warum machen wir das?</span> 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.

<span class="nb-accent">Warum ist das wichtig?</span> 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:

```bash
sudo apt update
```

**Danach installierst du den MariaDB-Server:**

```bash
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:**

```bash
sudo systemctl status mariadb
```

**Eine typische Ausgabe sieht so aus:**

```bash
● 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:**

```bash
sudo systemctl enable mariadb
```

Das sorgt dafür, dass MariaDB nach jedem Boot direkt verfügbar ist.

<blockquote class="infobox infobox--info">
💡 Führe `sudo systemctl status mariadb` regelmäßig aus, wenn du später Änderungen vornimmst. So erkennst du sofort, ob der Dienst läuft oder Probleme hat. 
</blockquote>

Nun sicherst du die Installation. Der Befehl `mysql_secure_installation` ist ein interaktives Skript, das die wichtigsten Schwachstellen schließt. Starte es mit:

```bash
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:

1. **Enter current password for root (enter for none):** Drücke einfach `Enter`, da noch kein Passwort gesetzt ist.
2. **Set root password? [Y/n]:** Gib `Y` ein und lege ein starkes Passwort fest. 
3. **Remove anonymous users? [Y/n]:** Gib `Y` ein. Anonyme Benutzer sind ein Sicherheitsrisiko.
4. **Disallow root login remotely? [Y/n]:** Gib `Y` ein. Root darf nur lokal zugreifen.
5. **Remove test database and access to it? [Y/n]:** Gib `Y` ein. Die Test-Datenbank ist nur für Experimente gedacht.
6. **Reload privilege tables now? [Y/n]:** Gib `Y` ein. Die Änderungen werden sofort wirksam.

<span class="nb-accent">Warum ist das wichtig?</span> 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:**

```bash
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:**

```markdown
/
└── 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.

<blockquote class="infobox infobox--practice">
❗ 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.
</blockquote>

**Du kannst den Port und die Bind-Adresse schnell prüfen:**

```bash
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:**

```bash
sudo tail -n 50 /var/log/mysql/error.log
```

Hier siehst du Start-Meldungen und eventuelle Warnungen direkt.

🔧 <span class="nb-accent">Praktisches Beispiel</span>

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:

```bash
# 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.

<span class="nb-accent">Was passiert hier?</span> 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.

<span class="nb-accent">Warum machen wir das?</span> 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.

<span class="nb-accent">Warum ist das wichtig?</span> 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:

```bash
sudo apt update
```

Danach installierst du [PHP](https://www.php.net/){.badge-link-text} in der Build Version `8.3` zusammen mit [FPM](https://www.php.net/manual/en/book.fpm.php){.badge-link-text} 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:

```bash
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:**

```bash
sudo systemctl status php8.3-fpm
```

Eine typische Ausgabe sieht so aus:

```bash
● 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:**

```bash
sudo systemctl enable php8.3-fpm
```

**Du kannst den Dienst bei Bedarf neu starten:**

```bash
sudo systemctl restart php8.3-fpm
```

<blockquote class="infobox infobox--info">
💡 Mit `php -v` prüfst du die installierte Version sofort im Terminal. Das gibt dir Sicherheit, dass alles korrekt läuft, bevor du die Integration mit Nginx startest. 
</blockquote>

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:**

```markdown
/
└── 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:**

```bash
php -v
```

**Die Ausgabe sollte etwa so aussehen:**

```bash
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.

<blockquote class="infobox infobox--practice">
❗ Ein typischer Fehler ist, nur `php8.3` ohne `-fpm` zu installieren. Dann fehlt der Prozess-Manager und Nginx kann keine PHP-Dateien ausführen. Achte immer auf die `-fpm`-Variante.
</blockquote>

**Du kannst die geladenen Module auflisten:**

```bash
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:**

```bash
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.

🔧 <span class="nb-accent">Praktisches Beispiel</span>

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:

```bash
# 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.

<span class="nb-accent">Was passiert hier?</span> 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.

<span class="nb-accent">Warum machen wir das?</span> 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.

<span class="nb-accent">Warum ist das wichtig?</span> 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:

```bash
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:**

```bash
sudo nano /etc/nginx/sites-available/lemp-test
```

Füge den folgenden kompletten Server-Block ein. 

**Jede Zeile wird danach erklärt:**

```nginx
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.

<blockquote class="infobox infobox--info">
💡 **Erklärung der Zeilen:** 

`listen 80` und `listen [::]:80` binden den Server an [IPv4](/de/netzwerk/oeffentliche-vs-private-ip-adresse-die-hauptunterschiede){.badge-link-text} und [IPv6](/de/netzwerk/oeffentliche-vs-private-ip-adresse-die-hauptunterschiede){.badge-link-text} auf Port 80. `server_name _` ist ein Platzhalter für alle Hostnamen. `root /var/www/html` legt den Dokumentenordner fest. `index` definiert die Standarddateien. Im `location /`-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.conf` lädt vordefinierte FastCGI-Parameter. `fastcgi_pass unix:/run/php/php8.3-fpm.sock` ist der entscheidende Socket-Pfad zu PHP-FPM. `location ~ /\.ht` verhindert Zugriff auf `.htaccess`-Dateien.
</blockquote>

**Aktiviere den neuen Server-Block mit einem symbolischen Link:**

```bash
sudo ln -s /etc/nginx/sites-available/lemp-test /etc/nginx/sites-enabled/
```

**Entferne den alten Default-Link, damit kein Konflikt entsteht:**

```bash
sudo rm /etc/nginx/sites-enabled/default
```

**Teste die Konfiguration auf Syntaxfehler:**

```bash
sudo nginx -t
```

**Eine erfolgreiche Ausgabe sieht so aus:**

```bash
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:**

```bash
sudo systemctl reload nginx
```

Der Reload übernimmt die Änderungen sofort.

<blockquote class="infobox infobox--info">
💡 Ü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. 
</blockquote>

**Du kannst den Socket-Pfad bestätigen, den PHP-FPM tatsächlich verwendet:**

```bash
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:**

```bash
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:**

```bash
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
```

Das verhindert Zugriffsfehler bei Dateien.

<blockquote class="infobox infobox--practice">
❗ Der häufigste Fehler ist ein falscher Socket-Pfad im `fastcgi_pass`. Wenn du `php8.3-fpm.sock` schreibst, aber der Socket heißt anders, kommt 502 Bad Gateway. Prüfe immer mit `ls /run/php/` den genauen Namen.
</blockquote>

**Die Log-Dateien geben Aufschluss bei Problemen:**

```bash
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:**

```markdown
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.

🔧 <span class="nb-accent">Praktisches Beispiel</span> 

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:**

```bash
#!/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.**

<span class="nb-accent">Was passiert hier?</span> 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.

<span class="nb-accent">Warum machen wir das?</span> 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.

<span class="nb-accent">Warum ist das wichtig?</span> 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:**

```bash
sudo mariadb -u root -p
```

**In der MariaDB-Shell gibst du ein:**

```sql
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:

```bash
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
<?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
}
?>
```
<blockquote class="infobox infobox--info">
💡 Jede PDO-Option wird hier bewusst gewählt. `PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION` sorgt dafür, dass Fehler nicht stillschweigend ignoriert werden, sondern als Exception geworfen werden – du siehst sie sofort. `PDO::ATTR_EMULATE_PREPARES => false` nutzt 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.
</blockquote>

<span class="nb-accent">Warum ist das wichtig?</span> 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:**

```bash
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:**

```bash
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):

```ini
[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
```

<span class="nb-accent">Warum machen wir das?</span> 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:**

```bash
sudo systemctl restart mariadb
```

**Prüfe die aktuellen Werte:**

```bash
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:**

```bash
mysql -u lemp_user -p -D lemp_test -e "SELECT COUNT(*) FROM test;"
```

Die Ausgabe zeigt die Anzahl der Einträge aus der Testtabelle.

<blockquote class="infobox infobox--practice">
❗ Vergiss nie, die Testdatei `db-test.php` nach erfolgreichem Test zu löschen oder stark zu sichern. Sie enthält sensible Informationen und darf niemals im Produktivbetrieb bleiben.
</blockquote>

**Ein Diagramm zeigt den kompletten Test-Flow:**

```markdown
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.

🔧 <span class="nb-accent">Praktisches Beispiel</span> 

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:

```bash
#!/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.

<span class="nb-accent">Was passiert hier?</span> 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.

<span class="nb-accent">Warum machen wir das?</span> 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.

<span class="nb-accent">Warum ist das wichtig?</span> 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:**

```bash
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:

```bash
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:**

```bash
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:**

```bash
sudo nano /etc/nginx/sites-available/lemp-test
```

Ersetze den bestehenden Block durch diese erweiterte Version mit starken Nginx-spezifischen Sicherheitsvorgaben:

```nginx
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](https://de.wikipedia.org/wiki/Clickjacking), [MIME-Sniffing](https://de.wikipedia.org/wiki/MIME-Sniffing) und [XSS-Angriffe](https://de.wikipedia.org/wiki/Cross-Site_Scripting).

**Teste die Konfiguration:**

```bash
sudo nginx -t
```

**Lade neu:**

```bash
sudo systemctl reload nginx
```

<blockquote class="infobox infobox--info">
💡 Nutze den Befehl `sudo certbot renew --dry-run` regelmäßig, um zu prüfen, ob die automatische Erneuerung funktioniert. Das spart später Überraschungen.
</blockquote>

**Für Performance-Tuning bearbeitest du die globale Nginx-Konfiguration:**

```bash
sudo nano /etc/nginx/nginx.conf
```

**Füge oder passe diese Zeilen im http-Block an:**

```nginx
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.

<blockquote class="infobox infobox--warn">
⚠️ Viele vergessen, nach Änderungen an der globalen Konfig `nginx -t` auszuführen. Ein Tippfehler kann den gesamten Webserver lahmlegen.
</blockquote>

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.

<blockquote class="infobox infobox--practice">
❗ 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.
</blockquote>

Das folgende Diagramm zeigt den sicheren Request-Flow nach der Konfiguration:

```markdown
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.

🔧 <span class="nb-accent">Praktisches Beispiel</span>

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:

```bash
#!/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](https://nginx.org/en/docs/){.badge-link-text}
[MariaDB Documentation](https://mariadb.com/docs/){.badge-link-text}
[PHP Documentation](https://www.php.net/docs.php){.badge-link-text}
[Let’s Encrypt Documentation](https://letsencrypt.org/docs/){.badge-link-text}
[Certbot Documentation](https://certbot.eff.org/){.badge-link-text}
[Ubuntu Server Guide](https://ubuntu.com/server/docs/){.badge-link-text}
[Ubuntu Packages](https://packages.ubuntu.com/){.badge-link-text}

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.** 