Projektdokumentation – Debian 12 YunoHost Härtung, Monitoring, Backup & MaxSecurity

Server-Härtung & Betrieb (Max-Security): Debian 12 + YunoHost + Nextcloud + WordPress + Mail

Ziel: Dieser Leitfaden dokumentiert die vollständige Härtung und den professionellen Betrieb ab Beginn der Härtungsphase (inkl. Monitoring, Architektur, Backup und Max-Security). Alle Maßnahmen sind für ein produktives VPS-Setup optimiert und bewusst ressourcenschonend gehalten.
🧱 Max-Security Hardening📈 Monitoring + E-Mail Alerts💾 Backup-Konzept + Restore-Plan🏗️ Architektur-Upgrade-Plan

Projektüberblick

Was wurde stabilisiert
I/O-Spitzen durch lokale WordPress-Backups beseitigt, Swap aktiviert, Repos konsolidiert, Redis + DB-Tuning, Rate-Limits, Kernel-Hardening, Alarmierung.
Was dieses Runbook liefert
Schritt-für-Schritt Hardening, wiederholbare Checks, Rollback, Wartungsplan, Restore-Plan und eine saubere Ziel-Architektur inkl. Migrationspfad.

1. Scope, Annahmen, Grundregeln

  • System: Debian 12 (Bookworm), YunoHost 12.x, nginx, php-fpm (8.3), MariaDB, Redis, Fail2Ban
  • Apps: Nextcloud (Hub), WordPress, Mail-Stack (Postfix/Dovecot)
  • Prämisse: Stabilität auf 2-GB-VPS (keine „Heavy-Suites“, kein Overkill, aber maximale Härtung mit vertretbarem Risiko)
  • Grundregel: Änderungen immer mit nginx -t/php-fpm -t prüfen, dann erst reload/restart.
Hinweis (YunoHost-Kompatibilität): Nginx/SSOwat/Nextcloud-Header und CSP nur YunoHost-konform anpassen. Zu aggressive CSP kann Nextcloud-Apps brechen.

2. Baseline-Checks (vor jeder Änderung)

Diese Checks geben dir in 60 Sekunden Klarheit, ob das System gesund ist.

uptime
free -h
df -h
sudo ss -tulpen | egrep ':22|:80|:443|:25|:587|:993|:53' || true
sudo systemctl status nginx --no-pager
sudo systemctl status php8.3-fpm --no-pager
sudo systemctl status mariadb --no-pager
sudo systemctl status redis-server --no-pager
sudo fail2ban-client status

3. Beginn der Härtungsphase (Kontext & Auslöser)

Auslöser: Instabilität/Timeouts (Nextcloud/YunoHost), „Zu viele Anfragen“, sporadische Verbindungsabbrüche. Haupttreiber: lokale WordPress-Backups (BackWPup) erzeugten I/O- und CPU-Spitzen, bei 2 GB RAM ohne Swap.
  • Disk-Analyse zeigte große Backup-Archive in /var/www/wordpress/wp-content/uploads/backwpup.
  • Swap wurde eingeführt, um Lastspitzen abzufedern.
  • APT-Repo-Mix (Sury/Yarn) wurde bereinigt, um Paketkonflikte zu verhindern.

4. Speicher & I/O stabilisieren (Root-FS, WordPress Backups)

4.1 Schnellanalyse: Wo liegt das Volumen?

sudo du -h --max-depth=1 /var | sort -hr
sudo du -h --max-depth=1 /var/www | sort -hr
sudo du -h --max-depth=2 /var/www/wordpress/wp-content/uploads | sort -hr | head -20

4.2 Maßnahme: lokale WP-Backups vermeiden

  • Ziel: Keine Multi-GB ZIP-Backups lokal auf dem VPS behalten.
  • Empfehlung: BackWPup so konfigurieren, dass Backups offsite (S3/FTP/Storage) gehen und lokal nur 0–2 Rotationen verbleiben.
Regel: Auf 2 GB RAM niemals „tägliches Vollbackup lokal als ZIP“ dauerhaft laufen lassen. Das erzeugt exakt die beobachteten Timeouts/Abbrüche.

5. Swap aktivieren (Stabilität bei Lastspitzen)

Status: Swapfile 2 GB ist aktiv. Leichte Swap-Nutzung ist normal und erhöht Stabilität.
sudo swapon --show
free -h
ls -lh /swapfile
grep -n swapfile /etc/fstab || true

5.1 (Optional) Swappiness konservativ setzen

echo "vm.swappiness=20" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p | tail -n 20

6. APT/Repository-Härtung (Stabilität statt Paketmix)

Ziel: Repos so minimal wie möglich halten. Jede zusätzliche Quelle erhöht das Risiko von Dependency-Brüchen.

6.1 Sury/Yarn entfernt bzw. deaktiviert

  • Sury-Repo: deaktiviert (404-Fehler, Versionsmix).
  • Yarn-Repo: entfernt (NO_PUBKEY). Nicht notwendig für Betrieb.
grep -R "sury" -n /etc/apt/sources.list /etc/apt/sources.list.d/* || true
ls -1 /etc/apt/sources.list.d
sudo apt update
sudo apt -f install -y
sudo apt autoremove --purge -y
sudo apt autoclean

7. Redis (Cache + Locking) – Nextcloud stabilisieren

Status: Redis läuft stabil (localhost), PHP-Modul geladen, Verbindung ok (redis-cli ping → PONG).

7.1 Verifikation

php -m | grep -i redis
redis-cli ping
sudo systemctl status redis-server --no-pager

7.2 Nextcloud auf Redis setzen (TCP localhost)

sudo yunohost app shell nextcloud
php occ config:system:set redis host --value="127.0.0.1"
php occ config:system:set redis port --value=6379 --type=integer
php occ config:system:set memcache.locking --value="\OC\Memcache\Redis"
php occ config:system:set memcache.distributed --value="\OC\Memcache\Redis"
php occ config:system:get redis
php occ config:system:get memcache.locking
exit

8. MariaDB – Performance & I/O reduzieren

Status: innodb_buffer_pool_size = 768M (805306368 Bytes) gesetzt – optimal für 2 GB RAM.

8.1 Verifikation

sudo mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
sudo mysql -e "SHOW VARIABLES LIKE 'max_connections';"
sudo systemctl status mariadb --no-pager

8.2 Empfohlene MariaDB-Parameter (2 GB RAM)

In /etc/mysql/mariadb.conf.d/50-server.cnf unter [mysqld] (nur, wenn nicht bereits gesetzt):

# /etc/mysql/mariadb.conf.d/50-server.cnf  (unter [mysqld])
innodb_buffer_pool_size = 768M
innodb_log_file_size = 256M
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 2
max_connections = 60
tmp_table_size = 64M
max_heap_table_size = 64M
sudo systemctl restart mariadb
sudo mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"

9. PHP-FPM – Prozesslimits für 2 GB RAM

Ziel: Verhindern, dass PHP-FPM durch zu viele Worker RAM frisst und das System in Swap- oder Timeout-Szenarien läuft.

9.1 Konfigurationspfad

  • Aktiv: php8.3-fpm
  • Pool: /etc/php/8.3/fpm/pool.d/www.conf

9.2 Empfohlene Werte

# /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 18
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
sudo php-fpm8.3 -t
sudo systemctl restart php8.3-fpm
sudo systemctl status php8.3-fpm --no-pager

10. Fail2Ban – Bruteforce & App-Schutz

Status: 11 Jails aktiv (u.a. nextcloud, wordpress, sshd, yunohost-portal). Das ist ein sehr guter Schutzgrad.
sudo fail2ban-client status
sudo fail2ban-client status sshd
sudo fail2ban-client status nextcloud
sudo fail2ban-client status wordpress

10.1 Default-Härtung (konservativ, produktionssicher)

# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 24h
findtime = 10m
maxretry = 3
sudo systemctl restart fail2ban
sudo fail2ban-client status

11. Nginx – Rate Limiting (DoS/Bot-Schutz) & Best Practices

11.1 Global Zone in nginx.conf (http{} Block)

# /etc/nginx/nginx.conf  (innerhalb http { ... })
limit_req_zone $binary_remote_addr zone=global_limit:10m rate=10r/s;

11.2 Anwendung im HTTPS server{} Block (443)

# innerhalb server { listen 443 ssl http2; ... } für mitcloud.mayit.eu
limit_req zone=global_limit burst=20 nodelay;
sudo nginx -t
sudo systemctl reload nginx
Wichtig: Zu strikte Limits können echte Nutzer (z.B. WebDAV Sync) beeinträchtigen. Bei Problemen: rate erhöhen oder burst anpassen.

12. Kernel Hardening (sysctl)

Empfohlene Baseline (produktionssicher). Block ans Ende von /etc/sysctl.conf:

# IP Spoofing Protection
net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.default.rp_filter=1

# Disable IP forwarding
net.ipv4.ip_forward=0

# SYN Flood Protection
net.ipv4.tcp_syncookies=1

# Disable source routing
net.ipv4.conf.all.accept_source_route=0
net.ipv6.conf.all.accept_source_route=0

# Ignore ICMP broadcast
net.ipv4.icmp_echo_ignore_broadcasts=1

# Log suspicious packets
net.ipv4.conf.all.log_martians=1
sudo sysctl -p
sudo sysctl net.ipv4.ip_forward
sudo sysctl net.ipv4.tcp_syncookies

13. Monitoring mit E-Mail Alarmierung

Status: Testmail erfolgreich. Monitoring läuft leichtgewichtig über ein Health-Check Script + Cron (alle 5 Minuten).

13.1 Mailutils (falls erforderlich)

sudo apt install -y mailutils
echo "Testmail Monitoring OK" | mail -s "Server Test" DEINE-MAIL@DOMAIN.DE

13.2 Health-Check Script

sudo nano /usr/local/sbin/server-health-check.sh
#!/bin/bash
MAIL="DEINE-MAIL@DOMAIN.DE"
HOSTNAME=$(hostname)

# RAM Check
RAM_USED=$(free | awk '/Mem:/ {printf("%.0f"), $3/$2*100}')
if [ "$RAM_USED" -gt 85 ]; then
  echo "RAM usage is ${RAM_USED}% on ${HOSTNAME}" | mail -s "ALERT: High RAM on ${HOSTNAME}" "$MAIL"
fi

# Disk Check
DISK_USED=$(df / | awk 'END{print $5}' | sed 's/%//')
if [ "$DISK_USED" -gt 85 ]; then
  echo "Disk usage is ${DISK_USED}% on ${HOSTNAME}" | mail -s "ALERT: High Disk on ${HOSTNAME}" "$MAIL"
fi

# Load Check
LOAD=$(uptime | awk -F'load average:' '{ print $2 }' | cut -d, -f1 | tr -d ' ')
LOAD_INT=${LOAD%.*}
if [ "$LOAD_INT" -gt 2 ]; then
  echo "Load average is ${LOAD} on ${HOSTNAME}" | mail -s "ALERT: High Load on ${HOSTNAME}" "$MAIL"
fi

# Service Check
for SERVICE in nginx php8.3-fpm mariadb redis-server
do
  if ! systemctl is-active --quiet "$SERVICE"; then
    echo "Service $SERVICE is NOT running on ${HOSTNAME}" | mail -s "ALERT: Service down on ${HOSTNAME}" "$MAIL"
  fi
done
sudo chmod +x /usr/local/sbin/server-health-check.sh
sudo /usr/local/sbin/server-health-check.sh

13.3 Cron (alle 5 Minuten)

sudo crontab -e
# hinzufügen:
*/5 * * * * /usr/local/sbin/server-health-check.sh

13.4 (Optional) Täglicher Statusreport

Empfehlung: ein kompakter Tagesreport (z.B. 08:00). Vorteil: du bemerkst Trends früh.

# /usr/local/sbin/server-daily-report.sh (Beispiel – optional)
# Sammle: uptime, df -h, free -h, fail2ban status, top 5 processes by mem/cpu, last 100 auth.log lines
# Versand per mail -s "Daily Report ${HOSTNAME}" "$MAIL"

14. Backup-Konzept (professionell) + Restore-Plan

Prinzip: Backups sind nur so gut wie der getestete Restore. Mindestens 1× pro Quartal Restore-Test durchführen und dokumentieren.

14.1 Backup-Ziele (Empfehlung)

KomponenteWas sichern?ZielRotation
YunoHostKonfiguration + Apps + Daten (YunoHost Backup)Offsite Storage (S3/FTP/Remote)täglich inkrementell / wöchentlich voll
NextcloudData Dir + DB Dump + config.php + custom appsOffsite + ggf. Snapshottäglich
WordPressDB + wp-contentOffsite (keine ZIP lokal)täglich/2-täglich
MailMaildirs + DB/Configs (je nach Setup)Offsitetäglich

14.2 YunoHost Backup (Standardweg)

sudo yunohost backup create --system --apps nextcloud,wordpress --name "daily-$(date +%F)"
sudo yunohost backup list
# Speicherorte prüfen (je nach YunoHost-Konfiguration): /home/yunohost.backup/ oder konfiguriertes Storage

14.3 Nextcloud konsistent sichern (App Maintenance Mode)

Strategie: Für konsistente Backups: Maintenance Mode + DB Dump + Files sichern. Bei großen Instanzen alternativ Snapshots mit kurzer Downtime.
sudo yunohost app shell nextcloud
php occ maintenance:mode --on

# DB Dump (lokal – danach offsite kopieren)
mysqldump --single-transaction -h localhost -u nextcloud -p"$(php -r 'include(\"/var/www/nextcloud/config/config.php\"); echo $CONFIG[\"dbpassword\"];')" nextcloud \
  > /home/yunohost.app/nextcloud/data/backup-nextcloud-db-$(date +%F).sql

php occ maintenance:mode --off
exit
Wichtig: DB-Passwort via config.php ist sensitiv. Sorge für sichere Dateirechte und offsite Transfer via verschlüsseltem Kanal.

14.4 Restore-Plan (Kurzform)

  • RTO/RPO definieren: z.B. RTO 4h, RPO 24h (je nach Anspruch)
  • Restore-Test: 1×/Quartal auf Test-VPS oder Snapshot
  • Reihenfolge: System → DB → App → Daten → Checks
# 1) System-Stand prüfen (Debian/YunoHost Version)
# 2) YunoHost Backup restore (system + apps)
# 3) Nextcloud: maintenance:mode on → DB import → files restore → files:scan → maintenance off
# 4) WordPress: DB import + wp-content restore
# 5) Smoke Tests: HTTP(S), login, webdav, cron, mail flow

15. Architektur-Upgrade-Plan (Roadmap)

Ist-Architektur: Alles auf einem 2-GB-VPS. Vorteil: einfach. Risiko: Resource-Contention (Backups, Updates, Peaks).

15.1 Ziel-Architektur (empfohlen)

VarianteBeschreibungVorteileRisiken/Notes
A (Minimal) VPS auf 4 GB RAM upgraden Einfachster Stabilitätsgewinn Kosten ↑, aber geringes Risiko
B (Empfohlen) WordPress auslagern (eigener VPS/Managed) Nextcloud erhält Ressourcen, weniger I/O-Spitzen DNS/SSL/Backup getrennt
C (Professionell) Mail separieren (oder managed), NC + WP getrennt Security-Isolation, bessere Wartbarkeit Komplexität ↑, aber best practice

15.2 Migrationspfad (Kurz)

  • WordPress Move: DNS TTL senken → Backup → Restore auf Ziel → Test → DNS Switch → Monitoring
  • Nextcloud bleibt: Redis/DB/Swap/Rate-Limits bleiben wie dokumentiert.
  • Mail: nur migrieren, wenn klare Notwendigkeit und saubere Restore-Tests möglich sind.

16. Max-Security Checkliste

BereichMaßnahmeStatusVerifikation
SSHKey-only, Root-Login aus, max tries reduziertOKsshd_config + Login-Test
Fail2Bansshd, nextcloud, wordpress, portal + recidiveOKfail2ban-client status
NginxRate limiting aktivOKnginx -T | grep limit_req
Kernelsysctl baseline hardeningOKsysctl …
PHP-FPMmax_children begrenzt + max_requestsOKphp-fpm8.3 -t + Prozesszählung
DBBuffer Pool 768M + flush optimiertOKSHOW VARIABLES
RedisCache + Locking (localhost)OKredis-cli ping
ReposKeine unsicheren/kaputten ReposOKapt update
MonitoringHealthcheck + Mail AlertsOKTestmail + Cron

17. Betriebsprozesse (Wartung, Updates, Notfall)

17.1 Regelmäßige Wartung (monatlich)

sudo apt update
sudo apt upgrade -y
sudo apt autoremove --purge -y
sudo yunohost diagnosis run
sudo fail2ban-client status
sudo journalctl -p 3 -xb --no-pager | tail -n 200

17.2 Nextcloud Wartung (monatlich/quartalsweise)

sudo yunohost app shell nextcloud
php occ status
php occ db:add-missing-indices
php occ maintenance:repair
exit

17.3 Incident Quick Response

# 1) Ressourcen
uptime
free -h
df -h
sudo iotop -oPa  # falls installiert

# 2) Web/DB/PHP
sudo systemctl status nginx php8.3-fpm mariadb redis-server --no-pager

# 3) Logs (kurz)
sudo tail -n 200 /var/log/nginx/error.log
sudo journalctl -u php8.3-fpm --since "30 min ago" --no-pager | tail -n 200
sudo journalctl -u mariadb --since "30 min ago" --no-pager | tail -n 200

# 4) Fail2Ban / Bruteforce
sudo fail2ban-client status nextcloud
sudo fail2ban-client status sshd

18. Rollback-Strategie

  • Nginx: Änderungen rückgängig → nginx -t → reload
  • PHP-FPM: www.conf zurück → php-fpm8.3 -t → restart
  • DB: Parameter zurück → restart → Buffer Pool ggf. reduzieren
  • sysctl: Werte entfernen → sysctl -p
  • Nextcloud: config.php Einträge (Redis) zurücksetzen, falls nötig

19. Appendix – Nützliche Befehle

# Welche Dienste laufen?
sudo systemctl list-units --type=service --state=running

# Welche PHP-FPM Units existieren?
sudo systemctl list-units | grep fpm

# Nginx Konfiguration komplett anzeigen (vorsichtig, sehr lang)
sudo nginx -T | head -n 120

# Nextcloud config Werte prüfen
sudo yunohost app shell nextcloud
php occ config:system:get trusted_domains
php occ config:system:get overwrite.cli.url
exit
Dokument-Standard: Dark/YunoHost-Style V2 (TOC fixiert, Copy-Buttons inkl. Fallback, Print-CSS).
Autor: MayIT
Hinweis: Bei jeder größeren Änderung: Backup + kurzer Smoke-Test (Login, WebDAV, Admin-Panel, Mailflow).