9.7 KiB
name, description, version, author, platforms
| name | description | version | author | platforms | ||
|---|---|---|---|---|---|---|
| nginx-vhost-deployment | Nginx-Webserver mit Name-based Virtual Hosting auf Ubuntu-Server installieren, konfigurieren und deployen – inkl. Fallstricke, DNS-Checks und Test-Workflow | 1.0.0 | Agent A (Deployer) |
|
Nginx Virtual Host Deployment
Installation und Konfiguration von nginx mit mehreren Virtual Hosts (name-based) auf einem Ubuntu-VPS. Deckt den gesamten Workflow ab: DNS-Prüfung → Installation → VHost-Konfiguration → Berechtigungen → Firewall → Test → SSL-Vorbereitung.
Auslöser (Trigger)
- „Webserver installieren"
- „Webseite online stellen"
- „Domain aufsetzen"
- „nginx einrichten"
- „Virtual Host konfigurieren"
- „Mehrere Domains auf einem Server"
Schritt 1: DNS-Check VOR der Installation
Immer zuerst prüfen, ob die Domains auf den Server zeigen – sonst arbeitet man ins Leere.
for domain in domain1.de domain2.de; do
echo "=== DNS: $domain ==="
dig +short A "$domain"
dig +short AAAA "$domain"
done
Interpretation:
- Zeigt auf richtige IP → ✅ Weiter
- Zeigt auf Cloudflare-IPs (104.x, 172.x) → ⚠️ Nutzer muss Cloudflare-Proxy deaktivieren (orange→grau)
- Zeigt auf fremde IP → ❌ Nutzer muss A-Record ändern
- Kein Eintrag → ❌ Nutzer muss A-Record anlegen
Server-IP herausfinden: curl -s ifconfig.me oder aus Memory/Konfiguration.
Schritt 2: nginx installieren
sudo apt-get update -qq
sudo apt-get install -y -qq nginx certbot python3-certbot-nginx
sudo systemctl enable --now nginx
NICHT vergessen: certbot und python3-certbot-nginx gleich mitinstallieren – spart später Arbeit.
Schritt 3: Verzeichnisstruktur pro Domain
sudo mkdir -p /var/www/DOMAIN.de/html
sudo mkdir -p /var/www/DOMAIN.de/logs
sudo chown -R www-data:www-data /var/www/DOMAIN.de/
sudo chmod -R 755 /var/www/DOMAIN.de/
⚠️ PITFALL – 403 Forbidden: Wenn nginx 403 zurückgibt, sind die Dateien/Berechtigungen schuld:
- Ursache: Dateien gehören
root:root, aber nginx-Worker läuft alswww-data - Fix:
sudo chown -R www-data:www-data /var/www/+sudo chmod -R 755 /var/www/ - Test:
ps aux | grep nginx→ Worker-User prüfen
Schritt 4: VHost-Konfiguration erstellen
⚠️ PITFALL – sites-available vs sites-enabled: SEPARATE Dateien, nicht nur Symlink
Auf manchen Setups sind sites-available/DOMAIN und sites-enabled/DOMAIN zwei
getrennte Dateien (kein Symlink). nginx lädt NUR sites-enabled/. Wer nur die
available-Datei editiert und reloadet, sieht keine Änderung — und nginx -t
meldet trotzdem OK (er testet die alte, aktive Datei → "grün" aber unverändert).
Symptom (2026-08-04 PersoEnt): neuer location /pb/ Proxy erschien nie über HTTPS,
nginx -T | grep -c "location /pb/" = 0, obwohl die available-Datei den Block hatte.
Diagnose (Symlink oder separate Datei?):
ls -la /etc/nginx/sites-enabled/DOMAIN # "-> sites-available/..." = Symlink, sonst separate Datei
nginx -T 2>/dev/null | grep -c "location /pb/" # 0 obwohl in available vorhanden => separate Datei aktiv
Fix: Endversion nach BEIDEN sites-available/... UND sites-enabled/... schreiben
(oder sauber als Symlink erzwingen: ln -sf .../sites-available/DOMAIN .../sites-enabled/DOMAIN),
dann nginx -t && systemctl reload nginx und mit nginx -T | grep verifizieren, dass der
neue Block wirklich in der aktiven Config steht.
⚠️ PITFALL – write_file verweigert Systempfade
write_file kann nicht nach /etc/nginx/sites-available/ schreiben (Systempfad-Schutz). Workaround: sudo tee im Terminal nutzen.
sudo tee /etc/nginx/sites-available/DOMAIN.de << 'EOF'
server {
listen 80;
listen [::]:80;
server_name DOMAIN.de www.DOMAIN.de;
root /var/www/DOMAIN.de/html;
index index.html index.htm;
access_log /var/www/DOMAIN.de/logs/access.log;
error_log /var/www/DOMAIN.de/logs/error.log;
location / {
try_files $uri $uri/ =404;
}
}
EOF
# Symlink aktivieren
sudo ln -sf /etc/nginx/sites-available/DOMAIN.de /etc/nginx/sites-enabled/
# Test + Reload
sudo nginx -t && sudo systemctl reload nginx
Template für neue VHosts liegt in templates/nginx-vhost.conf.
Schritt 5: Lokal testen (Host-Header-Trick)
Auch wenn DNS noch nicht propagiert ist, kann man den VHost lokal testen:
# HTTP-Status prüfen
curl -s -o /dev/null -w "HTTP %{http_code}\n" -H "Host: DOMAIN.de" http://localhost/
# Titel auslesen
curl -s -H "Host: DOMAIN.de" http://localhost/ | grep -oP '<title>\K[^<]+'
Erwartet: HTTP 200 + korrekter Seitentitel.
Schritt 6: Firewall (UFW) konfigurieren
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp # SSH nicht vergessen!
sudo ufw --force enable
sudo ufw status verbose
⚠️ PITFALL: ufw enable ohne vorher allow 22/tcp sperrt dich aus dem SSH aus! Immer zuerst SSH erlauben.
Schritt 7: Dem Nutzer DNS-Anpassungen mitteilen
Dem Nutzer eine klare Tabelle zeigen, welche DNS-Änderungen nötig sind:
| Domain | Aktuelles DNS | Aktion |
|---|---|---|
| domain.de | 104.21.x.x (Cloudflare) | A → SERVER-IP, Proxy deaktivieren |
| domain2.de | kein Eintrag | A-Record → SERVER-IP anlegen |
Server-IP: Immer explizit nennen (z. B. 92.60.38.175).
Schritt 8: SSL (nach DNS-Propagation)
Sobald DNS propagiert ist (5-60 Min):
sudo certbot --nginx -d DOMAIN.de -d www.DOMAIN.de --non-interactive --agree-tos --email ADMIN-EMAIL
Oder alle auf einmal:
sudo certbot --nginx -d domain1.de,www.domain1.de -d domain2.de,www.domain2.de ...
Certbot modifiziert automatisch die nginx-Konfiguration und fügt den listen 443 ssl Block hinzu.
⚠️ PITFALL – Certbot überschreibt SSL-Pfade in BESTEHENDEN vhosts
certbot --nginx für eine NEUE Domain kann die ssl_certificate-Pfade in den Configs
bestehender vhosts überschreiben. Certbot schreibt nicht nur den neuen vhost, sondern
modifiziert ggf. auch andere Dateien — besonders wenn server_name-Wildcards oder
*-Einträge involviert sind.
Symptom: Nach certbot --nginx -d neuedomain.de funktioniert https://alte-domain.de/
nicht mehr (SSL-Fehler, ERR_CERT_COMMON_NAME_INVALID). Die alte Config zeigt jetzt
auf /etc/letsencrypt/live/neuedomain.de/... statt .../alte-domain.de/....
Fix (nach JEDEM certbot-Lauf prüfen):
# Prüfen ob alle vhosts noch auf IHRE Zertifikate zeigen
for f in /etc/nginx/sites-enabled/*; do
cert=$(grep ssl_certificate "$f" | head -1 | grep -oP '/live/\K[^/]+')
domain=$(grep server_name "$f" | head -1 | awk '{print $2}')
echo "$domain → $cert"
# Prüfen ob das Zertifikat den Domain-Namen als SAN hat
openssl x509 -in /etc/letsencrypt/live/$cert/fullchain.pem -noout -text 2>/dev/null \
| grep -q "$domain" || echo " ⚠️ MISMATCH: $domain nicht in $cert!"
done
Reparatur: ssl_certificate und ssl_certificate_key im betroffenen vhost manuell
zurück auf das richtige Zertifikat setzen → nginx -t && systemctl reload nginx.
Prävention: Nach jedem certbot --nginx für eine neue Domain: Zuerst curl -sk https://alte-domain.de/ prüfen, bevor die neue URL getestet wird. Wenn alte Domains brechen, sofort reparieren (siehe Fix oben).
⚠️ PITFALL – Certbot-Henne-Ei bei NEUEM vhost (Zertifikat existiert noch nicht)
Wenn du den SSL-vhost vorab manuell anlegst (Referenz auf /etc/letsencrypt/live/DOMAIN/fullchain.pem), aber das Zertifikat noch nicht existiert, schlägt nginx -t fehl ([emerg] cannot load certificate ... No such file or directory). Dadurch schlägt auch certbot --nginx fehl (es läuft intern nginx -t → MisconfigurationError) — ein klassisches Henne-Ei.
Fix – nicht einen SSL-vhost vorab anlegen, erst Zertifikat mit HTTP-only-vhost holen:
- Temporär einen rein-HTTP-80-vhost anlegen (nur
listen 80;, kein SSL-Referenz):# in sites-available: nur listen 80, server_name, root sudo ln -sf .../sites-available/DOMAIN .../sites-enabled/ # oder in beide Dateien sudo nginx -t && sudo systemctl reload nginx # JETZT grün - Zertifikat per webroot holen (port-80-challenge, kein nginx-Plugin nötig):
sudo mkdir -p /var/www/DOMAIN.de/.well-known sudo certbot certonly --webroot -w /var/www/DOMAIN.de -d DOMAIN.de --agree-tos --register-unsafely-without-email - Erst danach den vollständigen SSL-vhost (mit
listen 443 ssl+ cert-Pfaden) ablegen +nginx -t && systemctl reload. - Verifizieren:
curl -sk https://DOMAIN.de→ HTTP 200 oder zumindest der SSL-Handshake ok (curl -sv ... | grep "SSL certificate verify ok").
Reihenfolge ist entscheidend: Erst Port-80 + Zertifikat existiert, DANN SSL-vhost. Vorab-SSL-vhost = Henne-Ei-Blocker.
⚠️ PITFALL – write_file verweigert /etc/systemd oder andere geschützte Pfade
Für systemd-Unit-Services (nicht nginx): write_file verweigert ebenso /etc/systemd/system/. Muster: In /tmp schreiben, dann sudo cp /tmp/foo.service /etc/systemd/system/ + systemctl daemon-reload.
Troubleshooting
| Symptom | Ursache | Fix |
|---|---|---|
| HTTP 403 | Falsche Datei-Rechte | chown -R www-data:www-data /var/www/ |
| HTTP 404 | index.html fehlt | Pfad in root-Direktive prüfen |
| „server_names_hash_bucket_size" | Zu viele/zu lange Domainnamen | sudo tee -a /etc/nginx/nginx.conf → server_names_hash_bucket_size 64; im http-Block |
| nginx startet nicht | Syntax-Fehler | sudo nginx -t zeigt die genaue Zeile |
| write_file „sensitive system path" | Systempfad-Schutz | sudo tee im Terminal nutzen |
| ufw inactive | Firewall nie aktiviert | sudo ufw --force enable |