Zum Inhalt springen
Serverküche
Suche

Die Suche wird geladen … (nur in der veröffentlichten Seite verfügbar).

Fehler-Finder: Selfhosting-Fehlermeldungen nachschlagen

Container startet nicht, Zertifikat fehlt, Mail hängt? Such deine Fehlermeldung – jede Antwort stammt aus einem Setup, das wir selbst zum Laufen gebracht haben.

Jede Karte hier stammt aus einem Tutorial dieser Seite – aus einem Fehler, der uns beim Aufsetzen auf dem Test-Server tatsächlich begegnet ist. Tipp die Fehlermeldung ein (address already in use, NXDOMAIN, Unauthorized) oder beschreib das Symptom in eigenen Worten.

264 Fundstellen aus echten Setups

In den Logs steht „middleware … does not exist" und der Dienst antwortet mit 404.

Fast immer fehlt der Provider-Zusatz: Eine im File-Provider definierte Middleware heißt aus Sicht der Docker-Labels name@file. Ohne Zusatz sucht Traefik sie unter den Docker-Labels – und findet nichts. Umgekehrt gilt dasselbe für name@docker.
Nachlesen: Traefik härten →

BasicAuth lehnt das richtige Passwort ab.

Die $-Zeichen im bcrypt-Hash wurden von Docker Compose als Variablen interpretiert. Im Compose müssen sie verdoppelt werden ($$2y$$05$$…). Mit docker compose config siehst du den Wert, der wirklich ankommt.
Nachlesen: Traefik härten →

Das Rate-Limit scheint nicht zu greifen.

Sequenzielle curl-Aufrufe sind zu langsam: Jeder Prozess baut eine neue Verbindung auf, damit bleibst du unter dem Limit. Teste parallel, etwa mit seq 40 | xargs -P 8 -I{} curl … – dann erscheinen die 429.
Nachlesen: Traefik härten →

Nach dem Setzen der IP-Allowlist bekommst du selbst 403.

Traefik sieht nicht deine IP, sondern die des davorstehenden Proxys (Cloudflare, Load-Balancer). Prüfe mit traefik/whoami, was in X-Forwarded-For steht, und arbeite bei mehrstufigen Aufbauten mit ipallowlist.ipstrategy.depth.
Nachlesen: Traefik härten →

Eine v2-Konfiguration mit ipWhiteList funktioniert nach dem Upgrade nicht mehr.

Die Middleware heißt in Traefik v3 ipAllowList; der alte Name wurde entfernt, die Regel greift damit nicht mehr. Beim Umstieg alle Vorkommen umbenennen.
Nachlesen: Traefik härten →

Der Browser kommt trotz korrigierter Konfiguration nicht mehr per HTTP durch.

Das ist HSTS und kein Fehler: Der Browser hat sich max-age gemerkt und erzwingt HTTPS selbst dann, wenn dein Server es nicht mehr anbietet. Zum Testen hilft ein privates Fenster oder ein anderer Browser – und deshalb die Warnung zu preload oben.
Nachlesen: Traefik härten →

Traefik antwortet mit 404, obwohl der Container healthy ist.

Meist fehlt traefik.http.services.dockge.loadbalancer.server.port=5001. Dockge lauscht im Container auf 5001; ohne die Zeile rät Traefik. Prüfe außerdem, dass der Container im proxy-Netz hängt.
Nachlesen: Dockge: Compose-Stacks im Browser verwalten →

Der Deploy bricht mit „no such file or directory" ab.

Host- und Container-Pfad des Stacks-Verzeichnisses stimmen nicht überein. Es muss /opt/stacks:/opt/stacks gemountet und DOCKGE_STACKS_DIR=/opt/stacks gesetzt sein – Dockge lässt den Docker-Daemon des Hosts arbeiten, und der kennt nur Host-Pfade.
Nachlesen: Dockge: Compose-Stacks im Browser verwalten →

Ein Stack ist sichtbar, aber alle Knöpfe fehlen.

Dann liegt er außerhalb von /opt/stacks, und Dockge zeigt „Dieser Stack wird nicht von Dockge verwaltet." Verschiebe den Ordner wie in Schritt 7 – Dockge übernimmt ihn nicht von selbst und legt auch keine Kopie an.
Nachlesen: Dockge: Compose-Stacks im Browser verwalten →

Der Stack-Name wird abgelehnt.

Erlaubt sind nur Kleinbuchstaben, Ziffern und Bindestriche, weil daraus Ordner- und Compose-Projektname werden. Meine App geht nicht, meine-app schon.
Nachlesen: Dockge: Compose-Stacks im Browser verwalten →

Nach einem Neustart ist die Anmeldung weg.

Dann wurde ./data nicht persistent gemountet – dort liegen Datenbank und JWT-Secret. Prüfe, dass /opt/dockge/data existiert und im Compose eingebunden ist.
Nachlesen: Dockge: Compose-Stacks im Browser verwalten →

Die Oberfläche ist englisch, obwohl der Browser auf Deutsch steht.

Dockge richtet sich nach dem Browser und fällt bei unbekannten Kombinationen auf Englisch zurück; in den Einstellungen lässt sich die Sprache fest wählen.
Nachlesen: Dockge: Compose-Stacks im Browser verwalten →

Die Bibliothek bleibt nach dem Anlegen leer.

Der erste Scan läuft nicht automatisch – das ist kein Fehler, sondern Audiobookshelfs Verhalten. Klick in der Bibliotheksansicht auf Bibliothek scannen. Bleibt sie danach leer, prüfe mit docker compose exec audiobookshelf ls -R /audiobooks, ob der Container die Dateien überhaupt sieht, und mit ls -ln /opt/audiobookshelf/audiobooks, dass alles UID 1000 gehört.
Nachlesen: Audiobookshelf: Hörbücher & Podcasts selbst hosten →

Aus vier MP3-Dateien werden vier Hörbücher statt vier Kapitel.

Die Dateien liegen direkt im Autoren-Ordner statt in einem gemeinsamen Titel-Ordner. Audiobookshelf gruppiert nach Autor/Titel/ – Dateien in einen Unterordner mit dem Buchtitel legen und erneut scannen.
Nachlesen: Audiobookshelf: Hörbücher & Podcasts selbst hosten →

Traefik antwortet mit 404, obwohl der Container läuft.

Meist fehlt traefik.http.services.abs.loadbalancer.server.port=80. Audiobookshelf lauscht im Container auf 80; ohne diese Zeile rät Traefik und trifft daneben. Prüfe außerdem, dass der Container wirklich im proxy-Netz hängt.
Nachlesen: Audiobookshelf: Hörbücher & Podcasts selbst hosten →

Die Adresse springt auf /audiobookshelf/.

Das ist normal: Das Frontend wird mit diesem festen Router-Pfad ausgeliefert, und ROUTER_BASE_PATH ändert daran zur Laufzeit nichts, weil der Pfad im Image eingebaut ist. https://DEINE_DOMAIN bleibt als Einstieg gültig.
Nachlesen: Audiobookshelf: Hörbücher & Podcasts selbst hosten →

Nach dem Gerätewechsel ist die Oberfläche wieder englisch.

Die Sprachwahl liegt lokal im Browser, nicht am Konto. Auf dem neuen Gerät einmal über den Benutzernamen → Language → „Deutsch" setzen.
Nachlesen: Audiobookshelf: Hörbücher & Podcasts selbst hosten →

Der Podcast wird nicht gefunden.

Die Suche fragt den iTunes-Katalog ab; ein Tippfehler oder ein dort fehlender Eintrag führt zu „Keine Suchergebnisse". Nimm stattdessen die RSS-URL des Podcasts – dasselbe Feld akzeptiert beides.
Nachlesen: Audiobookshelf: Hörbücher & Podcasts selbst hosten →

Redirect-Schleife (ERR_TOO_MANY_REDIRECTS) oder Mixed-Content-Warnungen.

Die HTTPS-Erkennung fehlt. Prüfe, dass der WORDPRESS_CONFIG_EXTRA-Block mit HTTP_X_FORWARDED_PROTO und den $$-Zeichen exakt gesetzt ist (siehe Warnung in Schritt 2), und starte den WordPress-Container neu.
Nachlesen: WordPress mit Docker & Traefik sauber betreiben →

WordPress startet nicht, meldet „Error establishing a database connection“.

Meist eine Race Condition oder ein Passwort-Mismatch. Prüfe, dass depends_on: condition: service_healthy gesetzt ist und WORDPRESS_DB_PASSWORD exakt MARIADB_PASSWORD entspricht. Logs: docker compose logs db.
Nachlesen: WordPress mit Docker & Traefik sauber betreiben →

Der erste Start scheitert, obwohl später alles läuft.

Die Datenbank braucht beim allerersten Start einige Sekunden zur Initialisierung. Mit dem Healthcheck ist das abgedeckt; ohne ihn hilft ein erneutes docker compose up -d.
Nachlesen: WordPress mit Docker & Traefik sauber betreiben →

Medien-Uploads schlagen bei großen Dateien fehl.

PHP begrenzt die Upload-Größe. Lege eine eigene uploads.ini an und mounte sie nach /usr/local/etc/php/conf.d/ mit z. B. upload_max_filesize = 64M und post_max_size = 64M.
Nachlesen: WordPress mit Docker & Traefik sauber betreiben →

Nach einem Domainwechsel zeigt die Seite ins Leere.

WP_HOME/WP_SITEURL in der Compose-Datei stehen fest verdrahtet – bei einer neuen Domain dort anpassen und Container neu starten (die Werte überschreiben die Datenbank-Einstellung).
Nachlesen: WordPress mit Docker & Traefik sauber betreiben →

Login scheitert mit CSRF-Fehler.

LD_CSRF_TRUSTED_ORIGINS: https://DEINE_DOMAIN fehlt oder ist falsch (muss mit https:// und ohne Pfad angegeben sein). Ergänzen und docker compose up -d (siehe Warnung in Schritt 1).
Nachlesen: linkding: Lesezeichen selbst hosten →

Die Seite kommt nicht (Traefik-404), obwohl der Container läuft.

Der Healthcheck ist noch nicht healthy – Traefik leitet dann bewusst nicht. Rund 30 Sekunden nach dem Start abwarten; Status mit docker inspect -f '{{.State.Health.Status}}' linkding-linkding-1 prüfen.
Nachlesen: linkding: Lesezeichen selbst hosten →

Titel/Beschreibung werden nicht automatisch geholt.

Die Zielseite blockiert automatisierte Zugriffe oder antwortet zu langsam. Du kannst Titel und Beschreibung dann einfach von Hand eintragen – die Felder stehen im selben Formular.
Nachlesen: linkding: Lesezeichen selbst hosten →

Das Admin-Konto wurde nicht angelegt.

LD_SUPERUSER_NAME/LD_SUPERUSER_PASSWORD greifen nur beim ersten Start mit leerer Datenbank. Existiert die data-Datenbank schon, ändert eine spätere Anpassung nichts. Konto nachträglich anlegen: docker compose exec linkding python manage.py createsuperuser.
Nachlesen: linkding: Lesezeichen selbst hosten →

Die Extension verbindet sich nicht.

Fast immer der API-Token oder die URL. Token unter Settings → Integrations neu erzeugen und exakt https://DEINE_DOMAIN als Server eintragen.
Nachlesen: linkding: Lesezeichen selbst hosten →

Die Bibliothek bleibt leer.

Entweder liegen keine Dateien im gemounteten music-Ordner, oder die Rechte stimmen nicht. Prüfe: docker compose logs | grep -i scan zeigt tracksImported. Ist es 0, kontrolliere den Pfad und dass der music-Ordner UID 1000 gehört (siehe Benutzer & Rechte).
Nachlesen: Navidrome: die eigene Musik streamen wie mit Spotify →

Titel erscheinen unsortiert oder ohne Cover.

Die Metadaten der Dateien sind unvollständig. Navidrome sortiert nach Tags, nicht nach Dateinamen – tagge die Dateien nach (z. B. mit Picard) und lass Navidrome erneut scannen.
Nachlesen: Navidrome: die eigene Musik streamen wie mit Spotify →

Neue Musik taucht nicht auf.

Der Scan läuft nur stündlich (ND_SCANNER_SCHEDULE). Einen sofortigen Scan stößt du in der Weboberfläche über das Aktualisieren-Symbol oben an, oder du wartest bis zum nächsten Intervall.
Nachlesen: Navidrome: die eigene Musik streamen wie mit Spotify →

Die App verbindet sich nicht.

Fast immer die Server-URL: Sie muss https://DEINE_DOMAIN lauten (mit https://, ohne Pfad). Prüfe außerdem, dass Benutzername/Passwort exakt stimmen – manche Apps verlangen zusätzlich die Aktivierung der Subsonic-Kompatibilität, die bei Navidrome standardmäßig an ist.
Nachlesen: Navidrome: die eigene Musik streamen wie mit Spotify →

Wiedergabe stockt bei großen FLAC-Dateien mobil.

Die Bandbreite reicht nicht fürs Original. Transcoding für den mobilen Player aktivieren (siehe Tipp in Schritt 7).
Nachlesen: Navidrome: die eigene Musik streamen wie mit Spotify →

Container startet nicht: „unable to open database file“.

DB_NAME: /data/hc.sqlite fehlt oder der data-Ordner gehört nicht UID 1000. Pfad setzen und chown -R 1000:1000 /opt/healthchecks/data (siehe Warnung in Schritt 2).
Nachlesen: Healthchecks einrichten →

Die Seite antwortet mit HTTP 500.

Häufigste Ursache: ein Umlaut in SITE_NAME (oder einer anderen Text-Umgebungsvariable). Auf reines ASCII umstellen und neu starten. Zur Diagnose vorübergehend DEBUG: "True" setzen – aber danach wieder auf False.
Nachlesen: Healthchecks einrichten →

Login schlägt fehl / CSRF-Fehler beim Absenden.

CSRF_TRUSTED_ORIGINS: https://DEINE_DOMAIN muss gesetzt sein – Django lehnt sonst POST-Anfragen hinter dem Reverse Proxy ab. Und ALLOWED_HOSTS muss exakt deine Domain enthalten.
Nachlesen: Healthchecks einrichten →

Der Check wird nicht „grün“, obwohl der Job läuft.

Prüfe, ob der curl-Ping wirklich ausgeführt wird und die richtige UUID trifft: curl -v https://DEINE_DOMAIN/ping/UUID sollte OK zurückgeben. Auf der Detailseite siehst du im Protokoll, ob und von welcher IP Pings ankommen.
Nachlesen: Healthchecks einrichten →

Ich bekomme keine Benachrichtigung bei „down“.

Es ist kein Integrations-Kanal zugewiesen, oder (bei E-Mail) fehlen die SMTP-Einstellungen. Unter „Integrations“ einen Kanal einrichten und dem Check zuweisen.
Nachlesen: Healthchecks einrichten →

Die Seite kommt nicht (Traefik-404), obwohl der Container läuft.

Der Healthcheck ist noch nicht healthy – Traefik leitet dann bewusst nicht. Nach dem Start 20–30 Sekunden warten; Status mit docker inspect -f '{{.State.Health.Status}}' stirling-stirling-pdf-1 prüfen.
Nachlesen: Stirling-PDF einrichten →

Login mit admin/stirling scheitert.

Entweder wurde das Passwort schon geändert (dann das neue nutzen), oder die Konfiguration im configs-Volume ist inkonsistent. Zum Zurücksetzen die Anwendung stoppen und die Nutzerkonfiguration im configs-Ordner prüfen; im Zweifel den Ordner leeren (Achtung: setzt alle Einstellungen zurück).
Nachlesen: Stirling-PDF einrichten →

OCR findet meine Sprache nicht.

Die passende .traineddata fehlt im tessdata-Volume. Die Datei von den Tesseract-Sprachpaketen holen und in den data-Ordner legen (siehe Schritt 5).
Nachlesen: Stirling-PDF einrichten →

Eine Umwandlung (z. B. Office → PDF) schlägt fehl.

Solche Konvertierungen brauchen zusätzliche Werkzeuge (LibreOffice), die nur in den größeren Image-Varianten enthalten sind. Für den vollen Funktionsumfang die -fat-Variante des Images verwenden (stirlingtools/stirling-pdf:2.14.3-fat).
Nachlesen: Stirling-PDF einrichten →

Große Dateien führen zu Fehlern oder langer Wartezeit.

OCR und Konvertierung sind speicherintensiv. Auf einem kleinen VPS kann RAM knapp werden – dann entweder kleinere Dateien verarbeiten oder auf ein größeres Produkt wechseln.
Nachlesen: Stirling-PDF einrichten →

Der Container startet nicht / Neustart-Schleife mit permission denied.

Die Rechte am config- oder data-Ordner stimmen nicht. chown -R 1000:1000 /opt/syncthing/config /opt/syncthing/data und neu starten (siehe Warnung in Schritt 1).
Nachlesen: Syncthing einrichten →

Die Oberfläche gibt eine Traefik-404, obwohl der Container läuft.

Der Healthcheck ist noch nicht healthy – Traefik leitet dann bewusst nicht. Bis zu 60 Sekunden nach dem Start abwarten und den Status prüfen (siehe Warnung in Schritt 3).
Nachlesen: Syncthing einrichten →

Zwei Geräte verbinden sich nicht.

Prüfe drei Dinge: Ist Port 22000 (TCP+UDP) in der Firewall offen? Hast du die ID auf beiden Geräten eingetragen (die Kopplung ist beidseitig)? Und stimmt die ID exakt (Buchstabendreher fallen leicht auf, weil eine Prüfsumme eingebaut ist)? Der Status „Getrennt“ mit korrekt eingetragenen IDs deutet fast immer auf den Firewall-Port.
Nachlesen: Syncthing einrichten →

Ein Ordner bleibt bei „Synchronisiere 0 %" hängen.

Meist fehlt auf der Gegenseite die Bestätigung der Freigabe, oder die Ordner-ID stimmt nicht überein – sie muss auf beiden Geräten identisch sein. Prüfe außerdem die Schreibrechte im Zielordner.
Nachlesen: Syncthing einrichten →

Nach dem Setzen des GUI-Passworts kein Zugriff mehr.

Passwort vergessen? Du kannst es in der config.xml (<gui>-Block) zurücksetzen, indem du die <user>- und <password>-Zeilen entfernst und Syncthing neu startest – danach ist die Oberfläche wieder ohne Login erreichbar (und du setzt es sofort neu).
Nachlesen: Syncthing einrichten →

Der Assistent meldet Schreibrechte-Fehler beim data-Ordner.

Der Container läuft als www-data (UID 33). Bei bind-gemountetem ./data müssen die Rechte passen: chown -R 33:33 /opt/freshrss/data (siehe Benutzer & Rechte).
Nachlesen: FreshRSS: der eigene RSS-Reader →

Feeds werden nicht automatisch aktualisiert.

Prüfe, ob CRON_MIN gesetzt ist und der interne Cron läuft: docker compose logs freshrss | grep -i cron. Manuell anstoßen kannst du jederzeit über den Aktualisieren-Knopf oben rechts oder docker compose exec -u www-data freshrss ./cli/actualize-user.php --user admin.
Nachlesen: FreshRSS: der eigene RSS-Reader →

Ein Feed bleibt leer / wird als „inaktiv" markiert.

Die Quelle liefert kein gültiges RSS/Atom, oder die URL ist eine HTML-Seite statt des Feeds. Viele Seiten verstecken den Feed – suche im Seitenquelltext nach application/rss+xml. Unter „Statistiken → Inaktive Feeds“ findest du Problemquellen gebündelt.
Nachlesen: FreshRSS: der eigene RSS-Reader →

Login klappt nicht, obwohl das Passwort stimmt.

FreshRSS verschlüsselt das Passwort im Browser (JavaScript). Ist JavaScript deaktiviert oder die Verbindung nicht wirklich HTTPS (Zertifikatswarnung), scheitert der Login. Über Traefik ist HTTPS gegeben – prüfe im Zweifel das Zertifikat.
Nachlesen: FreshRSS: der eigene RSS-Reader →

Nach einem Update fehlen Artikel.

FreshRSS bereinigt alte Artikel nach einer einstellbaren Frist (Standard: einige Wochen). Das ist gewollt und hält die Datenbank klein; erhöhe bei Bedarf die Aufbewahrung pro Feed in dessen Einstellungen.
Nachlesen: FreshRSS: der eigene RSS-Reader →

systemctl start schlägt fehl, status zeigt status=203/EXEC.

systemd konnte das Programm aus ExecStart nicht ausführen; im Journal steht dazu Unable to locate executable. Entweder stimmt der Pfad nicht, oder der Datei fehlt das Ausführbar-Bit (chmod +x). Ohne führenden / sucht systemd nur in /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin (nachzusehen mit systemd-path search-binaries-default) – ein eigenes Skript unter /opt oder /root musst du deshalb mit absolutem Pfad eintragen.
Nachlesen: systemd verstehen: Units, Journal & Timer (statt Cron) →

systemd warnt „The unit file, source configuration file or drop-ins of … changed on disk".

Du hast eine Unit-Datei bearbeitet, aber systemd liest Änderungen nicht automatisch. Nach jeder Änderung an /etc/systemd/system/ gilt: systemctl daemon-reload.
Nachlesen: systemd verstehen: Units, Journal & Timer (statt Cron) →

Der Timer taucht in list-timers nicht auf.

Er wurde nicht aktiviert. systemctl enable --now DIENST.timer – und daran denken, dass der Timer den Service auslöst, du also beide Dateien brauchst (.service und .timer).
Nachlesen: systemd verstehen: Units, Journal & Timer (statt Cron) →

systemctl enable antwortet „The unit files have no installation config".

Der Unit fehlt der Abschnitt [Install] mit WantedBy=; in systemctl status erkennst du sie am static in der Loaded:-Zeile. Ohne [Install] weiß systemd nicht, wann die Unit automatisch starten soll – bei Timern gehört WantedBy=timers.target hinein.
Nachlesen: systemd verstehen: Units, Journal & Timer (statt Cron) →

Das Journal ist riesig / frisst Platz.

Standardmäßig wächst es begrenzt, aber du kannst es beschneiden: journalctl --vacuum-time=14d löscht Einträge älter als 14 Tage, journalctl --disk-usage zeigt den Verbrauch.
Nachlesen: systemd verstehen: Units, Journal & Timer (statt Cron) →

„Host validation failed" statt Dashboard.

Die häufigste Stolperfalle: HOMEPAGE_ALLOWED_HOSTS fehlt oder enthält die falsche Domain. Genau die Domain eintragen, unter der du zugreifst, dann docker compose up -d.
Nachlesen: Homepage-Dashboard einrichten →

Die Ressourcen-Widgets zeigen keine oder falsche Werte.

CPU und RAM liest Homepage aus dem Container heraus (kein Socket nötig). Zeigt disk: / das Container-Dateisystem statt der Host-Platte, binde den gewünschten Pfad zusätzlich als Volume ein (z. B. - /:/host:ro und disk: /host).
Nachlesen: Homepage-Dashboard einrichten →

Ein Icon wird nicht geladen.

Der Name passt nicht zum Icon-Katalog. Schreibe den Dienstnamen klein und ohne Leerzeichen (nextcloud.png), oder lege ein eigenes Bild in den icons-Ordner (gemountet nach /app/public/icons) und referenziere es als /icons/name.png.
Nachlesen: Homepage-Dashboard einrichten →

Die Container-Kachel zeigt keinen Status.

Der Wert bei container: muss der exakte Container-Name sein. Finde ihn mit docker ps --format '{{.Names}}' – bei Compose ist das meist ordner-dienst-1 (z. B. traefik-traefik-1).
Nachlesen: Homepage-Dashboard einrichten →

Änderungen an den YAML-Dateien greifen nicht.

Ein Syntaxfehler bricht das Einlesen ab. YAML ist einrückungsempfindlich – prüfe die Logs mit docker compose logs homepage auf error-Zeilen und achte auf konsequente Leerzeichen (keine Tabs).
Nachlesen: Homepage-Dashboard einrichten →

Statt der Anmeldemaske kommt „Authentication error – Auth is disabled or misconfigured".

Dann ist HOMEPAGE_AUTH_ENABLED gesetzt, aber Secret oder Passwort kommen leer im Container an – meist, weil die .env im falschen Ordner liegt oder eine Zeile daraus fehlt. Im Log steht dazu Password auth is enabled but required settings are missing; docker compose config zeigt dir, welche Werte Compose wirklich einsetzt.
Nachlesen: Homepage-Dashboard einrichten →

Der Container bleibt unhealthy.

Fast immer der IPv6-Fallstrick aus Schritt 4: Der Healthcheck fragt localhost ab, nginx lauscht aber nur auf IPv4. Auf http://127.0.0.1/ umstellen. Prüfen mit docker inspect --format '{{.State.Health.Status}}' CONTAINER.
Nachlesen: Website mit Hugo hosten →

Traefik zeigt 404 page not found (statt deiner Seite).

Das ist die Traefik-404, nicht die von nginx – Traefik findet keinen passenden Router. Häufigste Ursachen: Der Container ist noch nicht healthy, die Host()-Regel enthält die falsche Domain, oder das proxy-Netz ist nicht external. Siehe das 502/404-Kapitel in Docker-Netzwerke verstehen.
Nachlesen: Website mit Hugo hosten →

Alle Links und Bilder sind kaputt.

Die baseURL in hugo.toml stimmt nicht mit der echten Domain überein. Hugo backt absolute URLs auf Basis dieses Werts ein – korrigieren und neu bauen.
Nachlesen: Website mit Hugo hosten →

Ein Beitrag erscheint nicht.

Prüfe das date im Front Matter: Liegt es in der Zukunft, baut Hugo den Beitrag nicht (siehe Warn-Box in Schritt 3). Auch ein draft: true versteckt Inhalte im normalen Build.
Nachlesen: Website mit Hugo hosten →

theme "PaperMod" not found beim Build.

Der Theme-Ordner fehlt im Build-Kontext – meist, weil er ein Git-Submodul ist, das nicht mitkopiert wurde, oder eine .dockerignore ihn ausschließt. Theme wie in Schritt 1 als echten Ordner ablegen.
Nachlesen: Website mit Hugo hosten →

Container erreicht anderen nicht per Name (bad address).

Beide hängen nicht im selben benannten Netz, oder einer nutzt das Standard-bridge (kein DNS). Prüfe mit docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' CONTAINER, in welchen Netzen ein Container hängt.
Nachlesen: Docker-Netzwerke verstehen →

Traefik gibt 502 Bad Gateway.

Fast immer sind Traefik und die App in verschiedenen proxy-Netzen, weil das Netz doppelt angelegt wurde. external: true im Compose sicherstellen und mit docker network inspect proxy prüfen, ob beide Container gelistet sind.
Nachlesen: Docker-Netzwerke verstehen →

network proxy declared as external, but could not be found beim up.

Das externe Netz existiert noch nicht. Einmalig anlegen: docker network create --ipv6 --subnet fd00:cafe::/64 proxy. Lass Traefik das Netz nicht selbst erzeugen – dann entsteht es ohne IPv6, und hinter dem Proxy steht bei IPv6-Besuchern die Gateway-Adresse statt der echten IP.
Nachlesen: Docker-Netzwerke verstehen →

Datenbank hat trotz internal Internet.

Sie hängt zusätzlich in einem nicht-internen Netz (z. B. proxy). Ein Container hat die Summe der Zugriffe aller seiner Netze – die DB gehört nur ins interne Netz, nie ins proxy-Netz.
Nachlesen: Docker-Netzwerke verstehen →

Zwei Stacks kollidieren beim Subnetz.

Docker vergibt 172.x-Bereiche automatisch, aber bei vielen Netzen kann der Pool knapp werden (could not find an available, non-overlapping IPv4 address pool). Ungenutzte Netze aufräumen: docker network prune.
Nachlesen: Docker-Netzwerke verstehen →

Permission denied, obwohl die Rechte stimmen.

Prüfe die Rechte des übergeordneten Verzeichnisses: Fehlt dort das x-Recht, kommst du gar nicht erst an die Datei heran, egal wie ihre eigenen Rechte aussehen. ls -ld /pfad/zum/ordner zeigt es.
Nachlesen: Linux-Benutzer, Gruppen & Dateirechte verstehen →

docker ps sagt weiter „permission denied“ nach usermod -aG docker.

Die neue Gruppe ist in der aktuellen Sitzung noch nicht aktiv – aus- und wieder einloggen (siehe Warn-Box in Schritt 7). id in der neuen Sitzung muss docker anzeigen.
Nachlesen: Linux-Benutzer, Gruppen & Dateirechte verstehen →

Neue Dateien haben unerwartete Rechte.

Das bestimmt die umask – sie zieht Rechte von den Vorgaben ab. Der Debian-Standard umask 0022 führt zu 644 für Dateien und 755 für Verzeichnisse. Prüfen mit dem Befehl umask (ohne Argument).
Nachlesen: Linux-Benutzer, Gruppen & Dateirechte verstehen →

Ein Container schreibt nicht ins Volume.

Der Prozess im Container läuft unter einer bestimmten UID (oft nicht root). Setze den Besitzer des Host-Verzeichnisses passend: chown -R 1000:1000 ./data – die richtige UID nennt die Doku des Images (Umgebungsvariablen wie PUID/PGID).
Nachlesen: Linux-Benutzer, Gruppen & Dateirechte verstehen →

usermod hat den Benutzer aus Gruppen geworfen.

Du hast -G ohne -a benutzt. -G ersetzt die Nebengruppen. Immer usermod -aG verwenden. Reparatur: die fehlenden Gruppen mit usermod -aG gruppe1,gruppe2 benutzer wieder ergänzen.
Nachlesen: Linux-Benutzer, Gruppen & Dateirechte verstehen →

Die deutsche Suche liefert nach dem Modellwechsel wirre Treffer.

Die Fotos sind noch (oder teilweise) mit dem alten Modell indexiert – alte und neue Vektoren vertragen sich nicht. Unter Auftrags-Schlangen → Intelligente Suche den Lauf Alle starten und abwarten, bis die Warteschlange leer ist. Genau dieser gemischte Zustand entsteht auch, wenn du nur speicherst und den Neu-Index vergisst.
Nachlesen: Immich optimieren (Teil 2) →

Mit einem nllb-…-Modell ist die Suche nicht besser als vorher.

Bekanntes Verhalten in v3.1.0: Die Sprachangabe, die diese Modelle erwarten, lässt sich in Immich nicht setzen (siehe Warn-Box in Schritt 3). Auf ein XLM-…- oder SigLIP2-Modell wechseln und neu indexieren.
Nachlesen: Immich optimieren (Teil 2) →

Der ML-Container beendet sich nach dem Modellwechsel mit exit 137.

Out of Memory: Das neue Modell passt nicht mehr neben Server, Datenbank und deine übrigen Dienste. Entweder ein kleineres Modell wählen oder mehr RAM – das XLM-Basis-Modell braucht im Betrieb real rund 2 GB nur für den ML-Container.
Nachlesen: Immich optimieren (Teil 2) →

Die externe Bibliothek bleibt nach dem Scan leer.

Fast immer stimmt der Pfad nicht: Als Importpfad gehört der Container-Pfad (/mnt/archiv) hinein, nicht der Host-Pfad. Prüfe mit docker compose exec immich-server ls /mnt/archiv, ob der Container die Dateien überhaupt sieht – kommt dort nichts, fehlt der Volume-Eintrag oder das docker compose up -d nach der Änderung.
Nachlesen: Immich optimieren (Teil 2) →

Das Handy sichert nur, wenn die App offen ist.

Auf Android killt die Akku-Optimierung den Hintergrund-Dienst (App ausnehmen, siehe Schritt 1); auf iOS fehlt die Hintergrundaktualisierung oder iOS priorisiert die App herunter – öfter öffnen hilft tatsächlich. Und: Standardmäßig wird nur im WLAN gesichert – unterwegs „fehlen" Fotos also nur scheinbar.
Nachlesen: Immich optimieren (Teil 2) →

Browser meldet NET::ERR_CERT_AUTHORITY_INVALID, curl sagt self-signed certificate.

Der Server liefert das eingebaute Traefik-Notzertifikat (TRAEFIK DEFAULT CERT) aus, weil die Ausstellung fehlschlug. Fast immer: Der A-/AAAA-Record zeigt auf die falsche IP (dig DEINE_DOMAIN prüfen) oder Port 80 ist in der Firewall zu – die HTTP-01-Challenge kommt nie an. Die genaue Ursache steht in den Traefik-Logs: docker compose logs traefik | grep -i acme.
Nachlesen: Wie HTTPS funktioniert →

Im issuer steht (STAGING) Ersatz Emmer YR2.

Die Test-CA von Let’s Encrypt ist noch aktiv – Browser vertrauen ihr absichtlich nicht. Staging-Zeile aus der Traefik-Konfiguration entfernen, acme.json leeren, Container neu starten (Details im Traefik-Tutorial).
Nachlesen: Wie HTTPS funktioniert →

certificate has expired.

Die automatische Erneuerung scheitert seit mindestens 30 Tagen – gleiche Ursachenliste wie beim ersten Fehler, nur lange unbemerkt. Falls die Meldung nur auf einem Gerät auftaucht: dessen Systemuhr prüfen, TLS ist zeitkritisch.
Nachlesen: Wie HTTPS funktioniert →

Let’s Encrypt meldet too many certificates already issued.

Rate-Limit gerissen, meist durch Experimentier-Schleifen gegen die Produktiv-CA. Auf die Staging-CA wechseln, bis das Setup steht – und acme.json ins Backup nehmen, statt Zertifikate neu auszustellen.
Nachlesen: Wie HTTPS funktioniert →

unable to get local issuer certificate auf einem alten Client.

Dem System fehlt das Root-Zertifikat aus Schritt 3 – der Root Store ist veraltet. Auf Debian: apt update && apt install ca-certificates.
Nachlesen: Wie HTTPS funktioniert →

Der Container startet nicht, Log sagt „listen udp :53: bind: address already in use".

systemd-resolved (oder ein anderer DNS-Dienst) belegt Port 53. Stub-Listener abschalten wie in Schritt 1, danach docker compose up -d erneut.
Nachlesen: AdGuard Home einrichten →

docker compose up scheitert mit „cannot assign requested address" für 10.8.0.1.

Das WireGuard-Interface wg0 mit 10.8.0.1 existiert (noch) nicht – Docker kann den Port nicht an eine nicht vorhandene Adresse binden. Zuerst das WireGuard-VPN einrichten und wg0 hochfahren (ip -br addr show wg0 muss 10.8.0.1 zeigen), dann den Container starten.
Nachlesen: AdGuard Home einrichten →

Nach dem Setup ist die Weboberfläche nicht mehr erreichbar.

Im Assistenten wurde der Admin-Port auf 80 (Vorschlag) statt auf 3000 gesetzt – Traefik leitet aber auf 3000. In ./conf/AdGuardHome.yaml unter http: die address auf 0.0.0.0:3000 korrigieren und docker compose restart.
Nachlesen: AdGuard Home einrichten →

DNS filtert nicht, obwohl das Gerät verbunden ist.

Der Browser nutzt DNS-over-HTTPS (DoH) und umgeht damit deinen Server komplett – Firefox und Chrome haben das teils standardmäßig aktiv. In den Browser-Einstellungen „Sicheres DNS" / „DNS über HTTPS" deaktivieren. Prüfen kannst du das mit dig @10.8.0.1 … (greift immer).
Nachlesen: AdGuard Home einrichten →

Eine Website ist plötzlich kaputt (leere Seiten, fehlende Bilder, kein Login).

Overblocking – eine Sperrliste blockt eine Domain, die die Seite wirklich braucht. Im Abfrageprotokoll die gesperrte Domain suchen, per Rechtsklick/Menü freigeben (Ausnahme) oder die zu aggressive Liste deaktivieren.
Nachlesen: AdGuard Home einrichten →

Der Runner startet neu und meldet „cannot ping the docker daemon … certificate is valid for docker, …, not fjr-docker".

Der DinD-Dienst heißt anders als docker, aber sein TLS-Zertifikat ist auf docker ausgestellt. Nenne den DinD-Service exakt docker (wie oben) und sprich ihn über DOCKER_HOST: tcp://docker:2376 an – dann passt der Name zum Zertifikat.
Nachlesen: Forgejo Actions: eigener CI/CD-Runner mit Docker →

Der Job startet, scheitert aber beim actions/checkout mit einem Verbindungsfehler.

Der Runner wurde mit einer internen Instanz-URL (http://forgejo:3000) registriert. Die Job-Container im DinD können diesen Namen nicht auflösen. Neu registrieren mit der öffentlichen URL https://DEINE_DOMAIN (.runner-Datei im Volume vorher löschen oder das Volume neu anlegen).
Nachlesen: Forgejo Actions: eigener CI/CD-Runner mit Docker →

Der Runner erscheint gar nicht in der Übersicht / die Registrierung schlägt fehl.

Falscher oder bereits verbrauchter Token, oder der Runner erreicht Forgejo nicht. Frischen Token holen (Schritt 1) und prüfen, dass der Runner-Container https://DEINE_DOMAIN erreicht (docker compose run --rm runner wget -qO- https://DEINE_DOMAIN/api/healthz).
Nachlesen: Forgejo Actions: eigener CI/CD-Runner mit Docker →

Ein Job bleibt ewig „wartend" (pending).

Kein Runner hat ein passendes Label. Der Workflow nutzt runs-on: docker, der Runner muss also das Label docker tragen. Labels beim Registrieren prüfen; in der Runner-Übersicht werden die Labels je Runner angezeigt.
Nachlesen: Forgejo Actions: eigener CI/CD-Runner mit Docker →

actions/checkout findet die Action nicht.

Forgejo lädt Actions aus einem konfigurierten Register (standardmäßig data.forgejo.org). Ist der Server komplett vom Internet abgeschnitten, schlägt das fehl. Ausgehenden HTTPS-Zugriff erlauben oder Actions in einem internen Register spiegeln.
Nachlesen: Forgejo Actions: eigener CI/CD-Runner mit Docker →

mail-tester zeigt „reverse DNS does not match".

Der PTR-Eintrag fehlt, ist noch nicht propagiert oder passt nicht zum A-Record. PTR im Provider-Panel auf mail.DEINE_DOMAIN setzen und mit dig -x prüfen; sicherstellen, dass mail.DEINE_DOMAIN vorwärts auf dieselbe IP zeigt.
Nachlesen: E-Mail-Zustellbarkeit einrichten →

DKIM schlägt fehl (DKIM: FAIL oder „no signature").

Der DNS-Record ist falsch übernommen (wirklich abgeschnittener Schlüssel, falscher Selector) oder noch nicht propagiert. Den Wert exakt aus der Mailserver-UI kopieren, Selector im Record (SELECTOR._domainkey) mit dem im Server konfigurierten abgleichen, dann dig gegenprüfen. Dass dig den Schlüssel als zwei Blöcke in Anführungszeichen zeigt, ist dabei normal und kein Fehler – siehe Schritt 3.
Nachlesen: E-Mail-Zustellbarkeit einrichten →

SPF „permerror" oder „too many DNS lookups".

Mehrere SPF-Records, oder zu viele include:-Verschachtelungen (Limit: 10 DNS-Lookups). Mit dig +short TXT DEINE_DOMAIN | grep spf1 prüfen, ob wirklich nur eine Zeile zurückkommt, auf einen SPF-Record konsolidieren und unnötige include: entfernen.
Nachlesen: E-Mail-Zustellbarkeit einrichten →

Mails an Outlook/Hotmail landen im Spam oder werden abgewiesen.

Kommt eine harte Abweisung mit 550 5.7.515 Access denied, sending domain … does not meet the required authentication level, ist es kein Reputationsproblem, sondern Microsofts Authentifizierungsregel ab 5.000 Mails pro Tag – SPF und DKIM müssen bestehen und ein ausgerichteter DMARC-Record (mindestens p=none) vorhanden sein; die Schwelle klebt an der Domain, auch wenn du später weniger sendest. Landen die Mails dagegen nur im Junk-Ordner, ist Microsoft schlicht streng mit neuen IPs: Geduld, wenig aber regelmäßig senden, ggf. Microsofts SNDS-/JMRP-Programm nutzen.
Nachlesen: E-Mail-Zustellbarkeit einrichten →

Deine IP steht auf einer Blockliste.

Die IP war vor dir bei einem Spammer, oder ein Postfach von dir versendet Spam. Auf den gängigen Blocklist-Checkern prüfen, bei berechtigtem Fund über das jeweilige Delisting-Formular entfernen lassen – und die Ursache (kompromittiertes Konto) abstellen.
Nachlesen: E-Mail-Zustellbarkeit einrichten →

Die Weboberfläche auf Port 8080 antwortet nicht.

Der Container ist noch am Hochfahren oder Port 8080 ist belegt/geblockt. docker compose ps prüfen, docker compose logs lesen und sicherstellen, dass die Firewall Port 8080 (und später 443) durchlässt. Bleibt die Log-Ausgabe nach dem Setup komplett leer, steht das Log-Ziel noch auf Log file – in der Konsole unter Settings → Telemetry → Tracers auf Console umstellen und neu starten.
Nachlesen: Stalwart: der schlanke Mailserver in einem Container →

Ich habe das Bootstrap-Passwort verpasst.

Es wird nur einmal geloggt. Setze in der compose.yaml ein STALWART_RECOVERY_ADMIN=admin:DEIN_PASSWORT unter environment: und starte mit docker compose up -d neu – damit hast du ein festes Recovery-Admin-Konto.
Nachlesen: Stalwart: der schlanke Mailserver in einem Container →

Kein TLS-Zertifikat, die HTTPS-Adresse zeigt eine Warnung.

Entweder erreicht Let’s Encrypt deinen Server nicht auf Port 443 (Stalwart nutzt die TLS-ALPN-01-Challenge über 443), oder einer der fünf Hostnamen aus Schritt 1 fehlt im DNS. Das Log nennt den Schuldigen beim Namen: ACME authentication error … NXDOMAIN looking up A for autoconfig.DEINE_DOMAIN. Fehlende Records nachtragen, Port 443 in der Firewall öffnen – Stalwart wiederholt die Zertifikatsanforderung danach automatisch.
Nachlesen: Stalwart: der schlanke Mailserver in einem Container →

Mails nach außen bleiben hängen, Logs zeigen Timeouts auf Port 25.

Dein Provider sperrt ausgehenden SMTP-Verkehr. Bei netcup ist die Default-Policy „netcup Mail block" (Port 25/465/587) schuld: Im SCP unter Firewall → Policys dem Server dieses netcup-Template abnehmen – kein Ticket nötig, siehe netcup-Firewall einrichten. Ohne das ist keine Zustellung an fremde Server möglich.
Nachlesen: Stalwart: der schlanke Mailserver in einem Container →

Andere Server nehmen deine Mails nicht an oder sie landen im Spam.

Fehlender oder falscher PTR-Eintrag, kein SPF/DKIM/DMARC. Das ist kein Fehler von Stalwart, sondern Sache der DNS-/Reputations-Konfiguration – siehe das Tutorial zur E-Mail-Zustellbarkeit.
Nachlesen: Stalwart: der schlanke Mailserver in einem Container →

generate_config.sh bricht mit Cannot find command 'jq' ab.

Das Paket jq fehlt. sudo apt install -y jq und den Generator erneut starten.
Nachlesen: Eigener Mailserver mit Mailcow: Setup von Grund auf →

Der Generator meldet „User declined to create daemon.json" und bricht ab.

Auf Hosts mit aktivem IPv6 hast du die Frage nach der IPv6-fähigen Docker-Konfiguration mit n beantwortet – ohne sie läuft mailcow dort nicht, sonst droht ein offenes Relay. Lege /etc/docker/daemon.json mit {"ipv6": true} an (Docker 28 und neuer), starte Docker mit sudo systemctl restart docker neu und führe ./generate_config.sh erneut aus – oder bestätige die Frage beim zweiten Anlauf einfach mit Enter, dann erledigt der Generator beides selbst.
Nachlesen: Eigener Mailserver mit Mailcow: Setup von Grund auf →

Die Webmail antwortet nur mit Unauthorized.

SOGo kennt deine Mail-Domain nicht: Es liest die Domainliste ausschließlich beim Start und wurde nach dem Anlegen der Domain nicht neu gestartet – in seinen Logs steht No authentication sources defined - nobody will be able to login. Starte SOGo über E-Mail → SOGo neustarten neu. Bleibt es dabei, hängt noch ein fehlgeschlagener Login im Cache: docker compose restart memcached-mailcow sogo-mailcow.
Nachlesen: Eigener Mailserver mit Mailcow: Setup von Grund auf →

Kein Zertifikat, acme-mailcow startet immer wieder neu.

Let’s Encrypt erreicht deinen Server nicht auf Port 80, oder der A-Record von mail.DEINE_DOMAIN stimmt nicht. Prüfe mit docker compose logs acme-mailcow, ob der Hostname und die IP passen, und dass Port 80 durch UFW und die netcup-Firewall offen ist.
Nachlesen: Eigener Mailserver mit Mailcow: Setup von Grund auf →

Mails nach außen werden nicht zugestellt, Logs zeigen Timeouts auf Port 25.

Dein Provider sperrt ausgehenden SMTP-Verkehr (üblich als Spam-Schutz). Bei netcup ist es die Default-Policy „netcup Mail block" (Port 25/465/587): Im SCP unter Firewall → Policys dem Server dieses netcup-Template abnehmen – kein Support-Ticket nötig, Details in netcup-Firewall einrichten. Ohne das kannst du keine Mails an fremde Server senden – Empfang und lokale Zustellung funktionieren trotzdem.
Nachlesen: Eigener Mailserver mit Mailcow: Setup von Grund auf →

Ports 80/443 lassen sich nicht binden (address already in use).

Auf dem Host läuft schon ein Webserver oder Reverse Proxy (z. B. Traefik). Für dieses Rezept gehört mailcow auf einen eigenen Server. Alternativ bindest du den nginx mit HTTP_BIND/HTTPS_BIND und HTTP_PORT/HTTPS_PORT auf einen lokalen Port um und stellst mailcow hinter den Proxy – das beschreibt mailcows Traefik-Anleitung. Die Mail-Ports liegen in beiden Fällen weiterhin direkt am Host.
Nachlesen: Eigener Mailserver mit Mailcow: Setup von Grund auf →

Container starten träge oder werden vom Kernel beendet (OOM).

Zu wenig RAM. Auf mindestens 6–8 GB gehen oder ClamAV in der mailcow.conf (SKIP_CLAMD=y) deaktivieren – der Virenscanner ist der größte Speicherfresser.
Nachlesen: Eigener Mailserver mit Mailcow: Setup von Grund auf →

Die App meldet „Cannot connect" oder der Web-Client bleibt leer.

Die NTFY_BASE_URL passt nicht zur aufgerufenen Adresse. Sie muss exakt deine öffentliche HTTPS-URL sein (https://ntfy.DEINE_DOMAIN, ohne Slash am Ende). Nach einer Änderung docker compose up -d erneut ausführen.
Nachlesen: ntfy einrichten →

curl liefert HTTP 401 oder 403.

Bei deny-all sind fehlende oder falsche Zugangsdaten der häufigste Fehler. Benutzer/Passwort prüfen (docker exec ntfy ntfy user list) und beim Senden -u BENUTZER:PASSWORT bzw. den Bearer-Token mitgeben. Ein 403 bedeutet, dass der Benutzer existiert, aber keine Rechte auf dieses Thema hat – dann mit ntfy access Rechte vergeben.
Nachlesen: ntfy einrichten →

Nachrichten kommen im Browser an, aber nicht als Push aufs Handy, wenn der Tab zu ist.

Browser-Push braucht erteilte Benachrichtigungsrechte und einen aktiven Service Worker; das ist unzuverlässig, sobald der Tab geschlossen ist. Für echtes „unterwegs"-Push die ntfy-App verwenden – sie hält die Verbindung im Hintergrund.
Nachlesen: ntfy einrichten →

502 Bad Gateway von Traefik.

Der Container ist noch nicht bereit oder hört auf dem falschen Port. Prüfe docker compose logs ntfy und dass das Label loadbalancer.server.port=80 gesetzt ist – ntfy lauscht im Container auf Port 80 (NTFY_LISTEN_HTTP=":80").
Nachlesen: ntfy einrichten →

429 Too Many Requests bei vielen Nachrichten kurz hintereinander.

ntfy begrenzt standardmäßig die Rate pro Absender, um Missbrauch zu verhindern. Bei einem privaten Server mit eigenen Skripten stößt du selten daran – ein Schleifen-Skript ohne Pause aber schon. Nachrichten bündeln statt im Sekundentakt zu feuern, oder die Limits gezielt über die NTFY_VISITOR_*-Umgebungsvariablen anheben (in der ntfy-Doku unter „Rate limiting" beschrieben). NTFY_BEHIND_PROXY: "true" ist Voraussetzung, damit das Limit pro echter IP statt pro Traefik-Container greift.
Nachlesen: ntfy einrichten →

Der Container schreibt, aber der Bind-Mount-Ordner auf dem Host bleibt leer.

Du hast einen relativen Pfad benutzt, der woanders zeigt als gedacht, oder Docker hat den Pfad neu als leeren Ordner angelegt. Bei docker run Bind-Mounts immer mit absolutem Pfad angeben (/opt/app/config, nicht config). Existiert dieser Host-Pfad noch nicht, legt Docker ihn stillschweigend als leeren Ordner an – prüfe also mit ls, dass du wirklich den richtigen erwischt hast. In der compose.yaml sind ./-Pfade dagegen völlig in Ordnung – sie sind relativ zur compose.yaml und damit eindeutig. Achtung außerdem: docker run -v config:/data (ohne / oder ./ davor) ist kein Bind-Mount, sondern legt ein Named Volume namens config an.
Nachlesen: Docker-Volumes vs. Bind-Mounts →

Permission denied, sobald der Container in den Mount schreiben will.

Der Prozess im Container läuft unter einer anderen UID als der Besitzer des Host-Ordners – typisch bei Bind-Mounts. Bei Named Volumes tritt das selten auf (Docker setzt die Rechte). Bei Bind-Mounts den Ordner dem passenden Benutzer geben (chown -R 1000:1000 /opt/app/data) oder im Image die user:-Angabe der Compose nutzen.
Nachlesen: Docker-Volumes vs. Bind-Mounts →

Nach docker compose down sind alle Daten weg.

Du hast docker compose down -v benutzt – das -v löscht die Named Volumes mit. Für einen normalen Neustart ohne -v arbeiten. -v bewusst nur einsetzen, wenn du wirklich bei null anfangen willst.
Nachlesen: Docker-Volumes vs. Bind-Mounts →

docker system prune hat Daten gelöscht.

docker volume prune bzw. docker system prune --volumes entfernt Volumes, an denen gerade kein Container hängt. Bei docker volume prune sind benannte Volumes standardmäßig geschützt – gelöscht werden nur anonyme Volumes; benannte fallen erst mit --all/-a weg. (Bei docker system prune steuert -a/--all dagegen die Images, nicht die benannten Volumes.) Ein weiteres starkes Argument fürs Benennen – ein sk-demo-vol übersteht ein versehentliches docker volume prune, ein anonymes Volume nicht. Trotzdem prune mit Volumes nur laufen lassen, wenn alle wichtigen Stacks aktiv sind, oder gezielt mit docker volume rm entfernen.
Nachlesen: Docker-Volumes vs. Bind-Mounts →

Der Browser zeigt einen Zertifikatsfehler oder 404 page not found.

Traefik hat das Let’s-Encrypt-Zertifikat noch nicht geholt, oder der DNS-Record zeigt nicht auf den Server. Prüfe mit dig statistik.DEINE_DOMAIN, dass die IP stimmt, und sieh in die Traefik-Logs. Den genauen Container-Namen aus deinem Traefik-Setup findest du mit docker ps | grep traefik, dann docker logs <container-name> (bei uns z. B. docker logs traefik-traefik-1). Die HTTP-Challenge scheitert, wenn Port 80 nicht von außen erreichbar ist – kontrolliere deine Firewall und die netcup-Firewall.
Nachlesen: Matomo selbst hosten →

Der Installer meldet SQLSTATE... Connection refused oder hängt bei der Datenbank.

MariaDB war beim ersten Start noch nicht bereit. Gib der DB einen Moment und lade die Seite neu. Prüfe mit docker compose logs db, ob dort ready for connections steht. Erscheint stattdessen ein Zugangsfehler, passen DB_PASSWORD in der .env und die bereits angelegte DB nicht mehr zusammen – dann hilft ein sauberer Neustart mit docker compose down -v (Achtung: löscht die Daten) und docker compose up -d.
Nachlesen: Matomo selbst hosten →

Warnung „Es sieht so aus, als ob die trusted_hosts-Einstellung nicht korrekt ist".

Matomo prüft aus Sicherheitsgründen, unter welchem Hostnamen es aufgerufen wird. Die Warnung erscheint, wenn du die Domain wechselst. Bestätige den korrekten Hostnamen über den Button in der Meldung – Matomo trägt ihn dann in config/config.ini.php ein.
Nachlesen: Matomo selbst hosten →

Im Dashboard tauchen keine Besuche auf.

Der Tracking-Code fehlt, ist falsch eingebaut, oder du besuchst deine Seite selbst (Matomo ignoriert dich, wenn deine IP ausgeschlossen ist). Öffne deine Website in einem privaten Fenster und prüfe im Netzwerk-Tab, ob ein Request an matomo.php rausgeht. In Matomo hilft Verwaltung → Diagnose → Tracking Fehlschläge.
Nachlesen: Matomo selbst hosten →

Das Dashboard lädt sehr langsam.

Die On-the-fly-Archivierung rechnet bei jedem Aufruf. Richte den Archivierungs-Cron aus Schritt 8 ein und stelle die Berichterstellung auf Cron um.
Nachlesen: Matomo selbst hosten →

Der Container startet immer wieder neu (Restarting), im Log steht bind: address already in use.

Der eingebaute SSH-Server kollidiert mit sich selbst, weil SSH_PORT und SSH_LISTEN_PORT nicht zusammenpassen. Setze beide auf denselben Wert (hier 2222) – dann startet Forgejo sauber.
Nachlesen: Forgejo: der eigene Git-Server hinter Traefik →

Der Container braucht ewig, bis er healthy ist.

Docker führt den ersten Healthcheck standardmäßig erst nach dem interval (30 s) aus – der Container sieht also 30 s+ „ungesund" aus, obwohl Forgejo längst in ~2 s bereit ist. Die Lösung steckt schon oben in der Compose: start_interval: 2s prüft während der Startphase im 2-Sekunden-Takt und schaltet sofort auf healthy, sobald die App antwortet. (Braucht Docker 25+ / Compose v2.20+ – auf Debian 13 gegeben.)
Nachlesen: Forgejo: der eigene Git-Server hinter Traefik →

Traefik liefert 502 Bad Gateway.

Fast immer der falsche Port: Forgejos Weboberfläche lauscht intern auf 3000, deshalb muss loadbalancer.server.port=3000 gesetzt sein und der Container im proxy-Netz hängen.
Nachlesen: Forgejo: der eigene Git-Server hinter Traefik →

Klon-Links zeigen localhost oder den falschen Port.

Dann stimmen ROOT_URL, SSH_DOMAIN oder SSH_PORT nicht. Korrigiere die Werte in der Compose und starte mit docker compose up -d neu.
Nachlesen: Forgejo: der eigene Git-Server hinter Traefik →

SSH-Klon scheitert mit Permission denied (publickey).

Der SSH-Server läuft, aber dein öffentlicher Schlüssel ist noch nicht im Konto hinterlegt (Schritt 6) – oder du hast den Port 2222 vergessen.
Nachlesen: Forgejo: der eigene Git-Server hinter Traefik →

Der Live-Verifier bleibt auf „Waiting" / keine Treffer im Dashboard.

Prüfe im Browser (Entwicklertools → Netzwerk), ob hk.js überhaupt geladen wird und der Sende-Request danach mit Status 2xx zurückkommt. Häufigste Ursachen: Das Snippet steckt nicht im HTML, die in HitKeep angelegte Site-Domain passt nicht zum Hostnamen der besuchten Seite, oder ein Ad-/Tracking-Blocker filtert den Aufruf. Da du unter deiner eigenen Domain hostest (First-Party), greifen die meisten Blocker nicht – manche Listen kennen aber den Pfad hk.js.
Nachlesen: HitKeep selbst hosten →

Alle Besucher kommen scheinbar von einer einzigen IP, Land und Provider stehen auf „(Unknown)".

Dann greift HITKEEP_TRUSTED_PROXIES nicht: Der angegebene Bereich enthält die IP deines Proxys nicht, und HitKeep wertet nur noch Traefiks Container-IP aus. Prüfe mit docker network inspect proxy, in welchem Subnetz der Proxy liegt – 172.16.0.0/12 deckt die üblichen Docker-Netze ab – und starte den Stack neu (docker compose up -d). Danach zählt wieder die echte Client-IP aus dem X-Forwarded-For-Header.
Nachlesen: HitKeep selbst hosten →

Traefik liefert 404 oder 502.

Ein 404 heißt meist, dass die Router-Regel nicht greift – prüfe, dass Host(...) deine echte Domain enthält und der Container im proxy-Netz hängt. Ein 502 deutet auf den falschen Port: HitKeep lauscht intern auf 8080, deshalb muss loadbalancer.server.port=8080 gesetzt sein.
Nachlesen: HitKeep selbst hosten →

Nach dem Login landest du wieder auf der Anmeldeseite (Login-Schleife).

Das ist fast immer eine Diskrepanz bei HITKEEP_PUBLIC_URL: Der Wert muss exakt der Adresse entsprechen, über die du HitKeep aufrufst (inklusive https://, ohne abschließenden Slash). Korrigiere die Variable und starte den Container neu.
Nachlesen: HitKeep selbst hosten →

Der Container startet nicht bzw. ist nicht healthy.

Sieh in die Logs: docker compose logs -f hitkeep. Ein fehlendes oder leeres HITKEEP_JWT_SECRET ist eine typische Startbremse – erzeuge eines wie in Schritt 1 und trage es ein.
Nachlesen: HitKeep selbst hosten →

In wg show erscheint nie ein latest handshake.

Der UDP-Port ist nicht erreichbar oder die Schlüssel passen nicht. WireGuard ist bei falschen Schlüsseln stumm – es gibt keine Fehlermeldung, nur keinen Handshake. Prüfe: sudo ufw status (Port 51820/udp offen?), die netcup-Firewall (eingehend UDP 51820), den Endpoint im Client (richtige öffentliche IP?) und dass privater und öffentlicher Schlüssel nicht vertauscht sind.
Nachlesen: WireGuard-VPN einrichten →

Handshake ist da, aber im Full-Tunnel kommt kein Internet an.

Fast immer das Routing/NAT. Ist net.ipv4.ip_forward=1 gesetzt (sysctl net.ipv4.ip_forward)? Läuft Docker mit – oder ist UFW aktiv –, greift die FORWARD-DROP-Falle aus dem Warnkasten oben; beide setzen die Forward-Policy auf DROP. Die Regeln müssen mit -I FORWARD 1 vor den bestehenden Ketten stehen. Stimmt das externe Interface im MASQUERADE (eth0 vs. etwas anderes)?
Nachlesen: WireGuard-VPN einrichten →

Full-Tunnel steht, aber DNS-Anfragen laufen weiter am Tunnel vorbei (DNS-Leak).

Ohne DNS =-Zeile fragt dein Gerät weiter die DNS-Server deines lokalen Netzes – im offenen WLAN sieht der Betreiber also weiter, welche Domains du aufrufst. Prüfe unter Linux mit resolvectl status, welcher DNS-Server dem Interface wg0 zugeordnet ist, oder mach einen Leak-Test (z. B. auf dnsleaktest.com): Tauchen dort fremde Resolver auf, trägst du in der Client-Konfig einen echten Resolver ein (DNS = 1.1.1.1) – oder den Server selbst (10.8.0.1), falls dort ein eigener DNS-Resolver läuft.
Nachlesen: WireGuard-VPN einrichten →

Der Tunnel steht, aber große Übertragungen (SSH, HTTPS, Downloads) hängen oder brechen ab.

Ein MTU-Problem. wg-quick setzt die MTU auf 1420; in manchen Netzen (DS-Lite, bestimmte Mobilfunknetze) ist das noch zu hoch. Setz testweise MTU = 1412 oder 1280 in der [Interface]-Sektion des Clients.
Nachlesen: WireGuard-VPN einrichten →

wg-quick up meldet resolvconf: command not found.

Die DNS =-Zeile braucht resolvconf. Entweder sudo apt install openresolv installieren oder die DNS-Zeile entfernen, wenn du den VPN-DNS nicht brauchst.
Nachlesen: WireGuard-VPN einrichten →

Die Verbindung schläft ein, sobald das Handy kurz nichts sendet.

Der Client sitzt hinter NAT/CGNAT. PersistentKeepalive = 25 in der Client-Konfig hält die Verbindung offen.
Nachlesen: WireGuard-VPN einrichten →

Deine Werte liegen deutlich unter unseren, besonders bei der CPU.

Ein VPS teilt sich die physische CPU. Prüfe die Steal Time (vmstat 1, Spalte st) und top (Zeile %st). Ist sie dauerhaft hoch, rechnen gerade Nachbarn auf demselben Host. Miss zu einer anderen Tageszeit erneut – oft ist der Unterschied dann weg.
Nachlesen: netcup VPS 1000 G12 im Benchmark →

Die Disk-Werte sind absurd hoch (z. B. „10 GB/s random read").

Dir fehlt --direct=1 – dann misst fio den RAM-Cache, nicht die NVMe. Immer mit direktem I/O testen, sonst sind die Zahlen wertlos.
Nachlesen: netcup VPS 1000 G12 im Benchmark →

lsblk zeigt für vda die Spalte ROTA=1 („rotierend") – ist das eine Festplatte statt NVMe?

Nein. Das ist ein Virtualisierungs-Artefakt: Der virtio-Treiber meldet die virtuelle Disk pauschal als rotierend. Die gemessenen 100k+ IOPS und 4 GB/s beweisen, dass echter Flash-Speicher dahintersteht.
Nachlesen: netcup VPS 1000 G12 im Benchmark →

Der Download ist viel langsamer als 2 Gbit/s.

Miss gegen einen nahen, schnellen Server (z. B. Falkenstein). Ein weit entferntes Ziel oder eine langsame Gegenstelle begrenzt die Messung, nicht dein VPS. Ein einzelner curl-Stream schöpft zudem nicht immer die volle Bandbreite aus.
Nachlesen: netcup VPS 1000 G12 im Benchmark →

Jeder Lauf liefert andere Zahlen.

Normal – Benchmarks schwanken. Miss mehrfach, verwirf den ersten („warmen") Lauf und nimm den Median. Vergleiche außerdem nur gleiche Tool-Versionen und Parameter miteinander.
Nachlesen: netcup VPS 1000 G12 im Benchmark →

docker compose pull zieht kein neues Image, obwohl es eine neue Version gibt.

Dein Tag zeigt auf eine feste Version (:1.37.2) oder auf einen Major-Tag (:18), unter dem es nur ein neues Major gäbe. pull holt nur, was derselbe Tag inzwischen zeigt. Für einen Versionssprung musst du den Tag in der compose.yaml selbst anheben.
Nachlesen: Den ganzen Docker-Stack sicher aktuell halten →

Die App startet nach dem Update nicht mehr oder wirft Datenbank-Fehler.

Meist ein Breaking Change oder eine fehlgeschlagene Migration. Prüfe docker compose logs, gleiche mit den Release-Notes ab. Tag auf die alte Version zurücksetzen und up -d; hat die neue Version die Daten schon migriert, das Backup aus Schritt 3 einspielen.
Nachlesen: Den ganzen Docker-Stack sicher aktuell halten →

Diun meldet nichts, obwohl Updates existieren.

Prüfe, dass die Container das Label diun.enable=true tragen und Diun den Socket lesen kann (DIUN_PROVIDERS_DOCKER=true, Socket gemountet). Für neuere Versions-Tags braucht der Container zusätzlich diun.watch_repo=true – ohne das sieht Diun nur Änderungen am exakt gepinnten Tag. Und: Was Diun beim ersten Mal sieht, wird nur in die Datenbank geschrieben, nicht gemeldet (watch.firstCheckNotif ist false) – zum Testen des Meldewegs einmalig DIUN_WATCH_FIRSTCHECKNOTIF=true setzen.
Nachlesen: Den ganzen Docker-Stack sicher aktuell halten →

Die Festplatte läuft voll, obwohl du regelmäßig docker image prune machst.

docker image prune räumt nur dangling Images ab, nicht die alten getaggten Versionen. Entferne sie gezielt mit docker rmi <image>:<tag> oder – mit Bedacht – docker image prune -a. Auch der Build-Cache (docker builder prune) kann wachsen.
Nachlesen: Den ganzen Docker-Stack sicher aktuell halten →

Beim Ziehen kommt toomanyrequests / ein Docker-Hub-Rate-Limit.

diun.watch_repo=true auf vielen Images fragt viele Tags ab. Setz das Watch-Intervall seltener (z. B. einmal täglich) und grenze mit diun.include_tags auf relevante Versionen ein, statt das ganze Repo abzuklopfen.
Nachlesen: Den ganzen Docker-Stack sicher aktuell halten →

Eine frisch gesetzte Sperre wirkt nicht, die IP bekommt weiter 200.

Der Bouncer cached die Entscheidungen und zieht neue im stream-Modus erst nach dem Pull-Intervall (bis zu ~60 Sekunden). Kurz warten. Wer die Reaktionszeit drücken will, senkt updateIntervalSeconds in der Middleware (Schritt 3).
Nachlesen: CrowdSec: moderne, kollaborative Angriffsabwehr →

cscli metrics show acquisition zeigt keine Zeile für die Log-Datei.

CrowdSec liest die Logs nicht – und weil cscli leere Tabellen weglässt, fehlt die Zeile komplett statt Lines read: 0 zu zeigen. Häufigste Ursache: der Container startete, bevor die access.log existierte; dann steht in docker logs crowdsec die Zeile No matching files for pattern /var/log/traefik/access.log, und CrowdSec nimmt die Datei auch später nicht von allein auf – cd ~/crowdsec && docker compose restart behebt das. Prüfe sonst, dass Traefik wirklich nach /var/log/traefik/access.log schreibt (Schritt 1), dass beide Container denselben Host-Pfad einbinden und dass die acquis.yaml genau diesen Pfad nennt.
Nachlesen: CrowdSec: moderne, kollaborative Angriffsabwehr →

Nach dem Neustart von Traefik bekommt jede App 403 – auch du selbst.

Der Bouncer konnte die LAPI beim Start nicht erreichen und blockt dann im Zweifel, statt ungeschützt durchzulassen. Im Traefik-Log steht dazu crowdsecQuery:unreachable mit der URL http://crowdsec:8080/v1/decisions/stream. Prüfe mit docker ps, dass der crowdsec-Container Up ist – sobald der nächste Pull klappt (bis zu ~60 Sekunden), gehen die Anfragen von allein wieder durch. Das ist sicher, macht CrowdSec aber zu einer kritischen Komponente: Läuft der Container nicht, ist deine Seite betroffen. restart: unless-stopped (oben gesetzt) holt ihn nach einem Server-Neustart automatisch zurück.
Nachlesen: CrowdSec: moderne, kollaborative Angriffsabwehr →

CrowdSec sperrt lauter harmlose oder gar keine echten Besucher – die erkannte IP ist immer die deines CDNs.

Sitzt ein CDN wie Cloudflare vor Traefik, sieht Traefik nur dessen IP. Dann die echte Client-IP aus dem X-Forwarded-For-Header lesen lassen: In der Middleware forwardedHeadersTrustedIPs mit den CDN-Netzen füllen – sonst sperrst du am Ende das CDN. Direkt an netcup (ohne CDN) ist hier nichts zu tun.
Nachlesen: CrowdSec: moderne, kollaborative Angriffsabwehr →

docker compose up -d bei Traefik meldet einen Plugin-Fehler.

Traefik lädt das Plugin beim Start aus dem Netz. Prüfe, dass der Server raus darf (die netcup-Firewall sperrt ausgehend nur SMTP, UFW ebenso wenig) und dass modulename und version exakt stimmen.
Nachlesen: CrowdSec: moderne, kollaborative Angriffsabwehr →

CrowdSec sperrt nichts, obwohl die Angriffe in der access.log stehen – oder es sperrt 172.18.0.1.

Dann sieht Traefik die echte Absenderadresse gar nicht. Das passiert bei IPv6-Besuchern, wenn das proxy-Netz ohne IPv6 angelegt wurde: Docker schiebt die Verbindung dann über einen Hilfsprozess und ersetzt die Quelladresse durch die des Bridge-Gateways. In der access.log steht dann bei allen IPv6-Zugriffen dieselbe 172.x.x.x, und eine Sperre darauf trifft entweder niemanden oder alle. Leg das Netz mit --ipv6 neu an; die Anleitung dafür steht im Reverse Proxy mit Traefik.
Nachlesen: CrowdSec: moderne, kollaborative Angriffsabwehr →

dd hat die falsche Festplatte überschrieben – Daten weg.

dd fragt nicht nach und heißt nicht umsonst spöttisch „disk destroyer". Prüfe das Ziel (of=) immer vorher mit lsblk, und verwechsle nie sda mit sdb. Es gibt kein Rückgängig – im Zweifel lieber dreimal lesen.
Nachlesen: Die wichtigsten Terminal-Befehle für deinen Server →

kill -9 hat einen Dienst beendet, aber die Datenbank ist danach beschädigt.

-9 gibt dem Prozess keine Chance, sauber zu schließen. Nutze immer zuerst kill ohne -9 und gib dem Prozess ein paar Sekunden. Erst wenn er wirklich hängt, folgt die harte Variante.
Nachlesen: Die wichtigsten Terminal-Befehle für deinen Server →

rsync hat viel mehr kopiert (oder gelöscht) als erwartet.

Der abschließende Schrägstrich entscheidet: rsync -a /quelle/ kopiert den Inhalt von quelle, rsync -a /quelle kopiert den Ordner mit hinein. Und --delete löscht im Ziel alles, was in der Quelle fehlt. Erst mit -n testen, immer.
Nachlesen: Die wichtigsten Terminal-Befehle für deinen Server →

Ein Werkzeug meldet „command not found".

Das Paket ist nicht installiert. Installiere es (siehe Tipp-Box oben) oder prüfe den Namen. Manche Tools wie iotop brauchen zudem sudo, um überhaupt Daten zu sehen.
Nachlesen: Die wichtigsten Terminal-Befehle für deinen Server →

Nach tmux ist bei erneutem Login „alles weg".

Du hast eine neue Sitzung gestartet statt dich anzuhängen. tmux ls listet laufende Sitzungen, tmux attach -t 0 hängt dich an die erste. Nur tmux allein erzeugt jedes Mal eine frische.
Nachlesen: Die wichtigsten Terminal-Befehle für deinen Server →

502 Bad Gateway beim Aufruf, obwohl der Container läuft.

Traefik erreicht den Container, trifft aber den falschen Port. Jellyfin lauscht auf 8096 – prüfe das Label traefik.http.services.jellyfin.loadbalancer.server.port=8096 auf Tippfehler. Kommt stattdessen nach ein paar Sekunden ein 504 Gateway Timeout, ist der Port nicht das Problem, sondern das Netzwerk: Dann fehlt networks: [proxy], und Traefik läuft gegen eine Adresse, die es gar nicht erreichen kann.
Nachlesen: Jellyfin selbst hosten →

Die Bibliotheken bleiben leer, obwohl Dateien da sind.

Fast immer ein Rechte-Problem. Der Container läuft als 1000:1000 (Schritt 4), die Medien müssen also diesem Benutzer gehören: sudo chown -R 1000:1000 /mnt/media. Und: In Jellyfin muss der Container-Pfad stehen (/media/filme), nicht der Host-Pfad. Danach unter Dashboard → Geplante Aufgaben „Alle Bibliotheken scannen" starten.
Nachlesen: Jellyfin selbst hosten →

Video ruckelt/puffert, docker stats zeigt Jellyfin bei ~100 % CPU.

Es wird transcodiert. Unter Dashboard → Wiedergabe siehst du im Aktivitätsmonitor, ob „Transcode" statt „Direct Play" steht. Stell die Client-Qualität auf Original, stell exotische Codecs auf ein breit unterstütztes Format um, oder gib dem Server mehr (dedizierte) Kerne.
Nachlesen: Jellyfin selbst hosten →

Nach einem Reboot ist die Mediathek weg und Jellyfin zeigt leere Ordner.

Der Block Storage wurde nicht eingehängt – meist ein fehlender oder falscher fstab-Eintrag (Schritt 2). Prüfe mit df -h /mnt/media und sudo mount -a. Die Zeile muss die UUID verwenden, nicht /dev/vdb1.
Nachlesen: Jellyfin selbst hosten →

Login-Seite lädt, aber die App findet den Server nicht / Links zeigen auf eine interne Adresse.

JELLYFIN_PublishedServerUrl fehlt oder ist falsch. Setz sie in der compose.yaml auf https://jellyfin.DEINE_DOMAIN und starte mit docker compose up -d neu (ein restart reicht nicht, damit die Environment-Änderung greift).
Nachlesen: Jellyfin selbst hosten →

Ein Target steht in Prometheus auf up = 0 bzw. „DOWN".

Prometheus erreicht den Exporter nicht. Bei cadvisor prüfe, dass er im selben monitoring-Netz läuft und Name und Port exakt stimmen (cadvisor:8080). Beim node-Job prüfe den extra_hosts-Eintrag am Prometheus-Service und das Target host.docker.internal:9100 – ohne beides findet Prometheus den node-exporter im Host-Netz nicht. Nach Änderungen an der prometheus.yml den Prometheus-Container neu starten: docker compose restart prometheus.
Nachlesen: Monitoring mit Grafana & Prometheus →

Das cAdvisor-Dashboard zeigt teils „No data" oder cAdvisor startet nicht.

Fehlen die Mounts oder privileged: true, sieht cAdvisor die Container nicht. Kontrolliere den cadvisor-Block (Mounts, devices: /dev/kmsg). Einzelne „No data"-Panels sind normal – manche Metriken (etwa CPU-Drosselung) gibt es nur, wenn du den Containern CPU-Limits gesetzt hast.
Nachlesen: Monitoring mit Grafana & Prometheus →

Grafana lädt hinter Traefik nicht richtig – Login schlägt fehl oder das Layout ist kaputt.

Fast immer stimmt GF_SERVER_ROOT_URL nicht. Sie muss exakt die öffentliche Adresse sein (https://grafana.DEINE_DOMAIN). Prüfe außerdem, dass die Traefik-Label loadbalancer.server.port=3000 gesetzt ist – Grafana lauscht intern auf 3000.
Nachlesen: Monitoring mit Grafana & Prometheus →

Grafana zeigt in den Panels „No data", obwohl die Targets up sind.

Meist der Zeitbereich. Ein frischer Stack hat noch keine Historie – stell oben rechts „Last 15 minutes" ein und warte ein paar Minuten, bis Daten aufgelaufen sind. Prüfe zudem, dass beim Dashboard-Import die Prometheus-Datenquelle gewählt war.
Nachlesen: Monitoring mit Grafana & Prometheus →

Der Speicherplatz auf dem Server wächst stetig.

Das ist die Prometheus-TSDB. Reduziere --storage.tsdb.retention.time (z. B. auf 15d) oder überwache weniger Ziele. Das prom_data-Volume wächst mit Anzahl der Metriken × Vorhaltezeit.
Nachlesen: Monitoring mit Grafana & Prometheus →

Uploads großer Videos brechen nach etwa einer Minute ab (Fehler 502 oder 499).

Traefiks readTimeout (Standard 60 s) greift. Setz ihn wie in Schritt 4 auf 600s (oder höher) und starte Traefik neu. Das ist mit Abstand der häufigste Immich-hinter-Proxy-Fehler.
Nachlesen: Immich selbst hosten →

Der immich-machine-learning-Container stürzt ab bzw. beendet sich mit „exit 137", Suche und Gesichtserkennung funktionieren nicht.

Zu wenig RAM – der Container wurde vom System beendet (Out of Memory). Gib dem Server mehr Speicher, oder deaktiviere ML, indem du den Service immich-machine-learning aus der compose.yaml entfernst. Immich läuft dann ohne Gesichtserkennung und intelligente Suche, aber Upload und Zeitleiste funktionieren normal.
Nachlesen: Immich selbst hosten →

Nach einem Update starten die Container nicht mehr oder melden Migrationsfehler.

Server, Machine-Learning und Datenbank müssen auf derselben Version laufen. Setz in der .env die neue IMMICH_VERSION und aktualisiere alle Container gemeinsam (docker compose pull && docker compose up -d). Ein Downgrade nach einer Migration ist nicht möglich – nur ein Rücksetzen aus dem Backup.
Nachlesen: Immich selbst hosten →

Immich zeigt „Wartungsmodus" / „Vorübergehend nicht verfügbar".

Immich v3 startet bei bestimmten Datenbank-Zuständen in einen Wartungsmodus. Im Log (docker compose logs immich-server) steht dann eine URL mit einem einmaligen Token (…/maintenance?token=…) – darüber meldest du dich am Wartungsmodus an und wählst „neu starten" bzw. beendest ihn. Danach ist die normale Oberfläche wieder da.
Nachlesen: Immich selbst hosten →

Container startet nicht, „permission denied" beim Datenbank- oder Upload-Verzeichnis.

UPLOAD_LOCATION oder DB_DATA_LOCATION haben die falschen Zugriffsrechte oder liegen auf einem Netzlaufwerk. Lege beide auf einer lokalen Platte an und stelle sicher, dass Docker hineinschreiben darf.
Nachlesen: Immich selbst hosten →

Beim Login erscheint „Forbidden (403)" oder „CSRF verification failed".

PAPERLESS_URL ist nicht oder falsch gesetzt. Sie muss exakt deiner HTTPS-Adresse entsprechen (https://paperless.DEINE_DOMAIN, ohne Schrägstrich am Ende). Nach der Korrektur docker compose up -d.
Nachlesen: Paperless-ngx selbst hosten: papierloses Büro mit OCR →

Dateien im Consume-Ordner werden nicht verarbeitet, „permission denied".

Häufigste Ursache: Die Ordner wurden nicht vorab angelegt (Schritt 3), sondern beim ersten Start vom Docker-Daemon erzeugt – dann gehören sie root. Mit sudo chown -R $(id -u):$(id -g) ~/paperless/consume ~/paperless/export gehören sie wieder dir. Ansonsten: USERMAP_UID/USERMAP_GID passen nicht zum Besitzer des Ordners – ermittle deine Kennung mit id -u und id -g, trag die Werte ein und starte neu.
Nachlesen: Paperless-ngx selbst hosten: papierloses Büro mit OCR →

Hochgeladene Dokumente bleiben „in Bearbeitung" hängen.

Die Hintergrundverarbeitung läuft über Redis. Prüfe, dass der broker-Container läuft, und sieh unter Dateiaufgaben nach der Fehlermeldung der fehlgeschlagenen Aufgabe.
Nachlesen: Paperless-ngx selbst hosten: papierloses Büro mit OCR →

Die Texterkennung liefert Unsinn oder erkennt nichts.

Falsche OCR-Sprache. Setz PAPERLESS_OCR_LANGUAGE=deu (oder deu+eng für gemischte Dokumente). Nur installierte Sprachen funktionieren.
Nachlesen: Paperless-ngx selbst hosten: papierloses Büro mit OCR →

Office-Dokumente (Word, Excel) werden nicht angenommen oder enden im Timeout.

Dafür sind Gotenberg und Tika zuständig. Prüfe, dass beide Container laufen und PAPERLESS_TIKA_ENABLED=1 samt der beiden Endpoint-Variablen gesetzt ist.
Nachlesen: Paperless-ngx selbst hosten: papierloses Büro mit OCR →

Beim Massenimport wird der Server sehr langsam, die CPU ist dauerhaft am Anschlag.

OCR ist rechenintensiv, und Paperless nutzt standardmäßig alle Kerne. Auf kleinen Servern kannst du die Last drosseln, indem du die Zahl der Worker bzw. Threads begrenzt (PAPERLESS_TASK_WORKERS, PAPERLESS_THREADS_PER_WORKER). Dann dauert der Import länger, aber die Oberfläche bleibt bedienbar.
Nachlesen: Paperless-ngx selbst hosten: papierloses Büro mit OCR →

Nach dem Traefik-Neustart erscheint der HSTS-Header nicht in der curl-Ausgabe.

Meist ist die Middleware nicht am Router. Prüfe, dass die Zeile traefik.http.routers.nc.middlewares beide Middlewares nennt (nc-dav,nc-secure) und dass die zwei nc-secure-Header-Labels im nc-app-Block stehen. Danach docker compose up -d nc-app. Traefik übernimmt geänderte Labels erst beim Neu-Erstellen des Containers.
Nachlesen: Nextcloud absichern (Teil 2) →

Der E-Mail-Test schlägt fehl („Es gab ein Problem beim Senden der E-Mail").

Fast immer Port/Verschlüsselung oder Authentifizierung. Kombiniere SSL/TLS mit Port 465 oder STARTTLS mit Port 587 – nicht kreuzweise. Nutzt dein Anbieter 2FA, brauchst du ein App-Passwort (siehe Schritt 4). Details stehen im Nextcloud-Log: docker exec -u www-data nc-app php occ log:tail 20.
Nachlesen: Nextcloud absichern (Teil 2) →

Du kommst nach dem Erzwingen von 2FA selbst nicht mehr rein.

Du hattest noch keinen zweiten Faktor eingerichtet. Hebe die Pflicht per Kommandozeile auf, richte deinen Faktor in Ruhe ein und schalte sie danach wieder scharf: docker exec -u www-data nc-app php occ twofactorauth:enforce --off.
Nachlesen: Nextcloud absichern (Teil 2) →

Im Brute-Force-Kasten steht eine 172.x.x.x-Adresse statt deiner echten IP.

TRUSTED_PROXIES passt nicht zum tatsächlichen proxy-Subnetz. Ermittle es mit docker network inspect proxy -f '{{(index .IPAM.Config 0).Subnet}}' und trag den Wert in der nc-app-Umgebung ein (Teil 1, Schritt 2). Sonst sperrt der Brute-Force-Schutz im Ernstfall den Proxy und damit alle Nutzer.
Nachlesen: Nextcloud absichern (Teil 2) →

Nach einem Reboot meldet Nextcloud „Redis went away" oder wird sehr langsam.

Der App-Container ist vor Redis gestartet. Stelle sicher, dass nc-redis in depends_on steht (Teil 1) und mit restart: unless-stopped läuft – dann fängt Docker den Startreihenfolge-Fall selbst ab.
Nachlesen: Nextcloud absichern (Teil 2) →

„Zugriff über eine nicht vertrauenswürdige Domäne" statt der Login-Seite.

Deine Domain steht nicht in trusted_domains. Prüfe NEXTCLOUD_TRUSTED_DOMAINS in der Compose-Datei. Nachträglich setzen geht per occ: docker exec -u www-data nc-app php occ config:system:set trusted_domains 1 --value=cloud.DEINE_DOMAIN.
Nachlesen: Nextcloud selbst hosten →

Der Sicherheits-Check meldet „Your web server is not set up properly to resolve .well-known/caldav".

Der CalDAV/CardDAV-Redirect greift nicht. Kontrolliere die nc-dav-Middleware-Labels (Schritt 3) und dass der Router sie über ...routers.nc.middlewares=nc-dav einbindet. Das Regex muss die volle https://…-URL matchen – redirectregex prüft die komplette URL, nicht nur den Pfad.
Nachlesen: Nextcloud selbst hosten →

Warnung „The ‘Strict-Transport-Security’ HTTP header is not set".

Der HSTS-Header fehlt. Setz ihn als Traefik-Middleware und häng sie an den Router: traefik.http.middlewares.nc-secure.headers.stsSeconds=15552000. Häng sie zusätzlich zur nc-dav-Middleware an den Router (...routers.nc.middlewares=nc-dav,nc-secure). Der Header muss vom Proxy kommen, nicht von Nextcloud. In Teil 2 richten wir genau diese nc-secure-Middleware vollständig ein (inklusive includeSubdomains).
Nachlesen: Nextcloud selbst hosten →

Große Uploads brechen ab oder enden mit einem Timeout / „413".

Das PHP-Limit ist zu klein. Erhöhe PHP_UPLOAD_LIMIT und PHP_MEMORY_LIMIT (Schritt 3) und starte den Container neu. In seltenen Fällen greifen diese Variablen nicht – dann ein eigenes php.ini-Snippet ins Image mounten.
Nachlesen: Nextcloud selbst hosten →

Beim ersten Start bricht die Installation mit „MySQL server has gone away" oder „Connection refused" ab.

Der App-Container war schneller als die Datenbank. Genau dagegen ist der healthcheck mit depends_on: condition: service_healthy da – prüfe, dass beide in deiner Compose-Datei vorhanden sind, und starte mit docker compose up -d neu.
Nachlesen: Nextcloud selbst hosten →

Bad Gateway (502) beim Aufruf von status.DEINE_DOMAIN.

Fast immer fehlt das Port-Label traefik.http.services.kuma.loadbalancer.server.port=3001 oder es steht ein falscher Port drin. Traefik erreicht den Container dann zwar, klopft aber am falschen Port an. Label prüfen und docker compose up -d erneut ausführen.
Nachlesen: Uptime Kuma installieren →

404 page not found statt Kuma.

Wie bei jeder App hinter Traefik: traefik.enable=true gesetzt? Container im proxy-Netzwerk? Stimmt die Domain in der Host(...)-Regel und zeigt der DNS-Record status.DEINE_DOMAIN auf den Server? Das Traefik-Dashboard zeigt unter „HTTP Routers", ob kuma registriert ist.
Nachlesen: Uptime Kuma installieren →

Die Oberfläche lädt, aber die Live-Aktualisierung ruckelt / bricht ab.

Kuma nutzt WebSockets. Traefik leitet die standardmäßig korrekt weiter – tritt das Problem trotzdem auf, liegt es meist an einem davorgeschalteten CDN/Proxy (z. B. Cloudflare im „Proxy"-Modus), der WebSockets blockt. Für den Direktbetrieb hinter Traefik ist keine Zusatzkonfiguration nötig.
Nachlesen: Uptime Kuma installieren →

Nach einem Neuaufsetzen sind alle Monitore weg.

Das kuma-data-Volume wurde gelöscht (z. B. durch docker compose down -v). Alle Konfiguration und Historie liegt allein in diesem Volume – deshalb steht es im nächsten Abschnitt ganz oben.
Nachlesen: Uptime Kuma installieren →

Der Container beendet sich sofort wieder, das Log sagt No persistent volume!.

Es fehlt das volumes:-Mapping. Vaultwarden bricht dann mit dem Kasten „It looks like you did not configure a persistent volume!" ab und beendet sich mit Exit-Code 1, damit deine Passwörter nicht in einem flüchtigen Container landen. Ergänze ./vw-data:/data wie in Schritt 2 und starte neu.
Nachlesen: Vaultwarden: eigener Passwortmanager hinter Traefik →

Die Web-Oberfläche zeigt „You need to enable HTTPS!" oder der Login scheitert mit Krypto-Fehlern.

Vaultwarden nutzt die Web-Crypto-API des Browsers, die nur in einem sicheren Kontext (echtes HTTPS) verfügbar ist. Du hast die Seite über http:// oder mit ungültigem Zertifikat aufgerufen. Stell sicher, dass Traefik ein gültiges Let’s-Encrypt-Zertifikat geholt hat (Traefik-Log prüfen) und du die Seite über https:// erreichst. Die DOMAIN-Variable muss ebenfalls mit https:// beginnen.
Nachlesen: Vaultwarden: eigener Passwortmanager hinter Traefik →

Das Admin-Panel weist dein Passwort ab, obwohl es stimmt.

Vermutlich sind die Dollarzeichen im ADMIN_TOKEN nicht verdoppelt. In der compose.yaml muss aus jedem $ ein $$ werden. Prüfe mit docker compose exec vaultwarden printenv ADMIN_TOKEN, wie der Token wirklich im Container ankommt – dort muss wieder ein einfaches $ stehen. (docker compose config hilft hier nicht: Es zeigt die Datei mit den doppelten $$.) Denk auch daran: Beim Login gibst du das Passwort ein, nicht den Hash.
Nachlesen: Vaultwarden: eigener Passwortmanager hinter Traefik →

Die Handy-App findet den Server nicht oder meldet „Server-URL ungültig".

Die Server-URL muss die vollständige https://-Adresse ohne abschließenden Pfad sein (https://vault.DEINE_DOMAIN). Prüfe außerdem, ob die Domain von außen per Browser erreichbar ist und das Zertifikat gültig ist – Apps sind bei Zertifikatsfehlern strenger als Browser.
Nachlesen: Vaultwarden: eigener Passwortmanager hinter Traefik →

Trotz SIGNUPS_ALLOWED=false konnte sich jemand registrieren.

Wahrscheinlich wurde die Einstellung im Admin-Panel gesetzt und überschreibt die Umgebungsvariable, oder der Container wurde nach der Änderung nicht neu gestartet. Kontrolliere den Wert unter Settings → General settings im Panel und starte mit docker compose up -d neu.
Nachlesen: Vaultwarden: eigener Passwortmanager hinter Traefik →

Du hast dich selbst ausgesperrt.

ignoreip fehlte oder enthielt die falsche IP. Verbinde dich über die Konsole im netcup SCP (unabhängig von SSH) und entsperre dich mit sudo fail2ban-client set sshd unbanip DEINE_EIGENE_IP. Trage deine IP anschließend in ignoreip ein und lade neu. Deshalb steht die Whitelist in Schritt 2 an erster Stelle.
Nachlesen: Fail2ban einrichten →

fail2ban.service startet nicht (systemctl status zeigt „failed").

Fast immer ein Tippfehler in jail.local. Prüfe die Syntax mit sudo fail2ban-client -t (Testmodus) – der Befehl nennt die fehlerhafte Zeile.
Nachlesen: Fail2ban einrichten →

Deine Werte aus jail.local greifen nicht.

Es wird nur 10 Minuten gesperrt, oder die eigene IP fliegt trotz ignoreip raus. Fast immer fehlt der Reload – der Dienst lief schon, bevor du die erste eigene Zeile geschrieben hast, und start/enable --now tut bei einem laufenden Dienst nichts. Was wirklich gilt, zeigen sudo fail2ban-client get sshd bantime (600 = Debian-Standard, 3600 = deine Stunde) und sudo fail2ban-client get sshd ignoreip; übernommen wird es mit sudo systemctl reload fail2ban.
Nachlesen: Fail2ban einrichten →

Es wird nie jemand gesperrt, obwohl das Log voller Fehlversuche ist.

Prüfe mit sudo fail2ban-client status sshd, ob Total failed überhaupt steigt. Bleibt es bei 0, findet der Filter die Einträge nicht – meist, weil eine veraltete Anleitung einen logpath auf eine nicht existierende Datei gesetzt hat. Auf Debian 13 den logpath aus jail.local entfernen und das Journal nutzen (Schritt 2).
Nachlesen: Fail2ban einrichten →

Sperren „wirken" nicht – die IP verbindet sich weiter.

Auf Debian 13 sperrt Fail2ban per nftables (banaction = nftables aus defaults-debian.conf). Sichtbar wird das mit sudo nft list table inet f2b-table: Dort steht die Kette f2b-chain und darin ein Adress-Set addr-set-sshd mit den gesperrten IPs. sudo iptables -L -n | grep f2b liefert dagegen nichts – das ist kein Fehler, sondern die falsche Werkzeugebene, an der ältere Anleitungen hängen bleiben. Fehlt die Tabelle ganz, hakt das Zusammenspiel mit der Firewall – Dienst neu starten und /var/log/fail2ban.log ansehen.
Nachlesen: Fail2ban einrichten →

subprocess ssh: Host key verification failed beim SFTP-Ziel.

Restic kann den SSH-Hostkey des Backup-Servers nicht bestätigen, weil er noch nicht bekannt ist. Verbinde dich einmal manuell (ssh BACKUP_BENUTZER@DEIN_BACKUP_HOST) und bestätige den Fingerprint – oder hinterlege ihn mit ssh-keyscan DEIN_BACKUP_HOST >> ~/.ssh/known_hosts. Danach läuft Restic durch.
Nachlesen: Restic-Backups einrichten: verschlüsselt und off-site →

wrong password oder repository does not exist.

RESTIC_REPOSITORY oder RESTIC_PASSWORD_FILE ist nicht gesetzt oder zeigt ins Leere – im systemd-Dienst also die EnvironmentFile prüfen. Ein per Hand gesetztes export gilt nur in der aktuellen Shell.
Nachlesen: Restic-Backups einrichten: verschlüsselt und off-site →

repository is already locked.

Ein abgebrochener Lauf hat eine Sperre hinterlassen. Prüfe, dass wirklich kein Backup mehr läuft, dann restic unlock. Niemals blind entsperren, während parallel ein Lauf aktiv ist.
Nachlesen: Restic-Backups einrichten: verschlüsselt und off-site →

Das Backup wird riesig / sichert Unsinn.

Grenze mit --exclude/--exclude-file ein (Caches, Logs, temporäre Dateien) und nutze --one-file-system, damit Restic nicht in gemountete Fremd-Dateisysteme abtaucht.
Nachlesen: Restic-Backups einrichten: verschlüsselt und off-site →

prune dauert ewig oder wurde abgebrochen.

Kein Grund zur Panik – prune ist wiederaufnehmbar und das Repository bleibt gültig. Starte es erneut und lass anschließend einmal restic check laufen. Trenne --prune bei großen Repos vom täglichen Backup (siehe Schritt 6).
Nachlesen: Restic-Backups einrichten: verschlüsselt und off-site →

Der Timer läuft, aber in Uptime Kuma kommt nie eine Erfolgsmeldung.

Sieh dir den letzten Lauf mit journalctl -u restic-backup.service an. Meist bricht das Skript vor dem curl-Aufruf ab (z. B. der DB-Dump schlug fehl) – dank set -euo pipefail stoppt es dann sauber, statt ein kaputtes Backup als Erfolg zu melden. Der ausbleibende Push ist also kein Bug, sondern genau das Alarmsignal, das du haben willst.
Nachlesen: Restic-Backups einrichten: verschlüsselt und off-site →

Im Browser „Zertifikat ungültig" oder Traefik-Log zeigt ACME-Fehler.

Die drei üblichen Gründe: (1) Der DNS-Record zeigt noch nicht auf den Server – dig +short DEINE_DOMAIN prüfen. (2) Port 80 ist von außen nicht erreichbar (Firewall/netcup-Firewall) – die HTTP-Challenge braucht ihn. (3) Du hast das Rate-Limit der Produktiv-CA gerissen – auf den Staging-Server wechseln (Tipp in Schritt 3), testen, dann zurück.
Nachlesen: Traefik einrichten →

404 page not found beim Aufruf der App-Domain.

Traefik kennt die Route nicht. Prüfe: Hat der Container traefik.enable=true? Hängt er im proxy-Netzwerk? Stimmt die Domain in der Host(...)-Regel exakt (inkl. Subdomain)? Das Dashboard (Schritt 7) zeigt unter „HTTP Routers", ob der Router registriert wurde.
Nachlesen: Traefik einrichten →

Der Browser zeigt Traefiks selbstsigniertes Notfall-Zertifikat; im Log steht permissions 644 for /acme.json are too open, please use 600.

Traefik läuft, hat aber den ACME-Resolver übersprungen – daher kein echtes Zertifikat. chmod 600 acme.json nachholen (Schritt 2) und Container neu starten.
Nachlesen: Traefik einrichten →

Keine App wird geroutet; im Traefik-Log wiederholt sich client version 1.24 is too old. Minimum supported API version is 1.40.

Deine Traefik-Version ist zu alt für deine Docker-Engine – der Docker-Provider kann den Socket nicht mehr abfragen. Aktuelles Docker (Engine 29, API-Level ≥ 1.40) braucht Traefik ≥ v3.6; deshalb nutzt dieses Tutorial traefik:v3.7. Gegen Docker 29 nachgestellt: v3.5.6 läuft in genau diesen Fehler, v3.6.25 spricht wieder sauber mit dem Socket. Ältere Tags wie v3.3 oder v3.5 funktionieren mit neuem Docker also nicht mehr – Image-Tag hochziehen und docker compose up -d erneut ausführen.
Nachlesen: Traefik einrichten →

Basic-Auth am Dashboard wird sofort wieder abgewiesen / Router fehlt.

In der compose.yaml müssen die $-Zeichen des Hashes verdoppelt sein ($$). Prüfe den Hash außerhalb noch einmal mit htpasswd -nbB.
Nachlesen: Traefik einrichten →

Gateway Timeout oder Traefik erreicht den Container nicht.

Meist hängt die App im falschen Netzwerk oder Traefik weiß nicht, welches gemeint ist. providers.docker.network=proxy in Traefik und networks: [proxy] an der App müssen zusammenpassen.
Nachlesen: Traefik einrichten →

502 Bad Gateway, obwohl der Container läuft.

Traefik erreicht den Container, trifft aber den falschen Port. Lauscht die App nicht auf 80, braucht sie das Label traefik.http.services.<name>.loadbalancer.server.port=<echter-port>. Genau dieser Fall begegnet dir bei der ersten App im nächsten Tutorial (Uptime Kuma auf 3001).
Nachlesen: Traefik einrichten →

Der Probelauf meldet No packages found that can be upgraded unattended.

Meist völlig normal – aktuell steht kein Sicherheitsupdate an. Prüfe die Konfiguration trotzdem über die Allowed origins are:-Zeile im --debug-Lauf. Erscheinen dort keine Security-Origins, stimmt das Origins-Pattern aus Schritt 3 nicht.
Nachlesen: unattended-upgrades einrichten →

Updates kommen, aber der Server startet trotz Kernel-Update nie neu.

Meist steht Automatic-Reboot auf "false" (Standard) – dann auf "true" setzen und eine Automatic-Reboot-Time vergeben (Schritt 3). Steht die Option richtig, prüfe die Markierungsdatei: Ohne /var/run/reboot-required löst der Neustart nie aus. Dafür zuständig ist der Kernel-Hook des Pakets – dpkg -S /etc/kernel/postinst.d/unattended-upgrades muss ihn melden (Schritt 3).
Nachlesen: unattended-upgrades einrichten →

/boot läuft voll, Updates schlagen fehl.

Alte Kernel häufen sich an. Remove-Unused-Kernel-Packages "true" setzen (Schritt 3); einmalig aufräumen mit sudo apt autoremove --purge.
Nachlesen: unattended-upgrades einrichten →

Ein Paket wird hartnäckig zurückgehalten (kept back).

unattended-upgrades installiert keine Updates, die andere Pakete entfernen würden. Solche Fälle löst du bewusst von Hand mit sudo apt upgrade und prüfst, was passiert.
Nachlesen: unattended-upgrades einrichten →

yaml: line 7: did not find expected key (oder ähnliche YAML-Fehler).

YAML ist einrückungssensibel – ausschließlich Leerzeichen, niemals Tabs, und pro Ebene konsistent (üblich: 2 Leerzeichen). Prüfe die Datei ohne sie zu starten: docker compose config löst alles auf und meckert genau die falsche Zeile an.
Nachlesen: Docker Compose verstehen: Services, Volumes, Netzwerke →

Error ... address already in use beim up.

Der Host-Port (links in 8080:80) ist schon belegt. Finde den Beleger mit sudo ss -tlnp | grep 8080 oder wähle einen anderen Host-Port. Zwei Container dürfen sich denselben Host-Port nicht teilen.
Nachlesen: Docker Compose verstehen: Services, Volumes, Netzwerke →

Eine App findet ihre Datenbank nicht (could not translate host name).

Als Hostname muss der Service-Name stehen (z. B. db), nicht localhost. Innerhalb eines Containers ist localhost der Container selbst, nicht der Nachbar-Service. Und: Beide Services müssen im selben Compose-Projekt (derselben Datei) liegen.
Nachlesen: Docker Compose verstehen: Services, Volumes, Netzwerke →

Nach docker compose down sind alle Daten weg.

Entweder lag das Volume nicht als Named Volume unter volumes: vor (dann war es nur der vergängliche Container-Speicher), oder es wurde down -v verwendet. Persistente Dienste immer mit deklariertem Named Volume fahren.
Nachlesen: Docker Compose verstehen: Services, Volumes, Netzwerke →

docker-compose: command not found.

Das ist das alte Compose v1 (mit Bindestrich). Aktuell ist docker compose (mit Leerzeichen, Plugin). Falls es fehlt: sudo apt install docker-compose-plugin (siehe Docker-Tutorial).
Nachlesen: Docker Compose verstehen: Services, Volumes, Netzwerke →

dig liefert keine oder eine falsche (alte) IP.

Meist noch Propagation. Frage gezielt einen öffentlichen Resolver, der deinen lokalen Cache umgeht: dig +short DEINE_DOMAIN @1.1.1.1. Zeigt der schon den richtigen Wert, ist nur dein lokaler/Provider-Cache noch nicht abgelaufen – abwarten.
Nachlesen: Eine Domain mit dem Server verbinden (DNS-Grundlagen) →

Die Domain zeigt zwar, aber die falsche IP – z. B. eine Parking-Seite des Registrars.

Oft existiert noch ein alter A-Record oder eine Weiterleitung/Parking-Einstellung. Entferne widersprüchliche Einträge; pro Name und Typ sollte genau ein Wert stehen (bewusste Ausnahmen ausgenommen).
Nachlesen: Eine Domain mit dem Server verbinden (DNS-Grundlagen) →

IPv4 klappt, IPv6 (AAAA) führt zu Timeouts.

Du hast einen AAAA-Record eingetragen, obwohl der Server keine funktionierende IPv6-Adresse hat. Browser bevorzugen dann IPv6 und laufen in einen Timeout. AAAA-Record entfernen, bis der Server wirklich IPv6 kann.
Nachlesen: Eine Domain mit dem Server verbinden (DNS-Grundlagen) →

In manchen Oberflächen wird der Wert mit angehängtem Punkt gespeichert (DEINE_DOMAIN.) und du bist unsicher.

Der abschließende Punkt (Root der DNS-Hierarchie) ist normal und korrekt – viele DNS-Verwaltungen ergänzen ihn automatisch. Kein Grund zur Sorge.
Nachlesen: Eine Domain mit dem Server verbinden (DNS-Grundlagen) →

Vom Server verschickte E-Mails landen im Spam oder werden abgelehnt (im Mail-Log steht z. B. does not resolve to address oder no PTR record).

Der Reverse-DNS-Eintrag (PTR) fehlt oder passt nicht zum Absender-Hostnamen. Setze den PTR wie in Schritt 5 beschrieben beim VPS-Anbieter und achte darauf, dass er auf denselben Namen zeigt, der auch per A-/AAAA-Record auflöst.
Nachlesen: Eine Domain mit dem Server verbinden (DNS-Grundlagen) →

Nach ufw enable kommst du per SSH nicht mehr rein.

Die SSH-Regel fehlte oder betraf den falschen Port. Verbinde dich über die Konsole im netcup SCP (VNC, unabhängig von SSH), erlaube dort deinen SSH-Port (sudo ufw allow …) und teste erneut. Genau davor warnt Schritt 3.
Nachlesen: Firewall mit UFW einrichten →

ERROR: Could not find a profile matching 'OpenSSH'.

Das OpenSSH-Profil existiert nur, wenn openssh-server installiert ist. Nutze stattdessen die Portnummer: sudo ufw allow 22/tcp (bzw. deinen Port). sudo ufw app list zeigt die verfügbaren Profile.
Nachlesen: Firewall mit UFW einrichten →

Ein Docker-Container ist von außen erreichbar, obwohl UFW den Port nicht freigibt.

Kein Fehler von dir – Docker umgeht UFW. Docker schreibt seine Regeln direkt in iptables und hängt sie vor die UFW-Ketten. Ein veröffentlichter Container-Port (ports: in der compose.yaml) ist damit offen, egal was UFW sagt. Die saubere Lösung ist eine zweite Firewall-Ebene vor dem Server – bei netcup die Firewall im SCP als vorgelagerter Perimeter. Auf dem Host hilft außerdem, Container-Ports nur an 127.0.0.1 zu binden statt an 0.0.0.0.
Nachlesen: Firewall mit UFW einrichten →

„Erstellen" legt keinen Snapshot an, solange eine DVD eingelegt ist.

Hängt unter Medien → DVD-Laufwerk ein .iso-Image im virtuellen Laufwerk, verweigert das SCP neue Snapshots. Wirf das Image dort aus – in der Server-Übersicht muss die Zeile ISO wieder - zeigen – und starte den Snapshot erneut.
Nachlesen: netcup Server Control Panel: Snapshots, Konsole & Rettung →

Der Snapshot lässt sich nicht anlegen, weil der Speicherplatz nicht reicht.

Ein Snapshot braucht Platz neben deinen Live-Daten. Räume auf dem Server auf, gib die freigewordenen Blöcke mit fstrim -av an den Host zurück, lösche nicht mehr benötigte Snapshots und stoße im SCP unter Medien → Festplatten die Speicheroptimierung an.
Nachlesen: netcup Server Control Panel: Snapshots, Konsole & Rettung →

Nach einem Online-Snapshot-Restore ist die Datenbank beschädigt.

Ein Online-Snapshot fängt einen laufenden Schreibvorgang womöglich mittendrin ein. Für Server mit Datenbank nimm einen Offline-Snapshot oder erstelle vorher einen Datenbank-Dump.
Nachlesen: netcup Server Control Panel: Snapshots, Konsole & Rettung →

Du kommst nicht ins SCP.

Das SCP nutzt eigene Zugangsdaten (nicht SSH). Sie stehen in der Willkommensmail; ein vergessenes SCP-Passwort setzt du im netcup-Kundenkonto (CCP) zurück.
Nachlesen: netcup Server Control Panel: Snapshots, Konsole & Rettung →

Nach dem Aktivieren geht keine Namensauflösung mehr (apt update hängt, ping domain.de scheitert, aber ping 1.1.1.1 geht).

Die DNS-Antworten werden geblockt. Prüfe im Basis-Template die Regel EINGEHEND UDP ACCEPT, Src-Port 53. Wichtig: Quell-Port, nicht Ziel-Port.
Nachlesen: netcup-Firewall einrichten →

Die Uhr driftet, oder TLS-Zertifikate werden wegen falscher Zeit abgelehnt.

Die NTP-Antworten fehlen. Ergänze EINGEHEND UDP ACCEPT, Src-Port 123.
Nachlesen: netcup-Firewall einrichten →

IPv6 funktioniert nicht mehr (v4 schon).

Es fehlt die ICMPv6-Regel. Ohne Neighbor Discovery kann der Server über IPv6 nicht einmal seinen Nachbarn (Router) finden.
Nachlesen: netcup-Firewall einrichten →

Du kommst nach dem Aktivieren nicht mehr per SSH rein.

Die SSH-Regel fehlt oder nennt den falschen Port. Über die VNC-Konsole (SCP → Bildschirm) kommst du trotzdem auf den Server; korrigiere die Policy und speichere neu. Genau dagegen hilft die „zweite Sitzung offen lassen"-Regel oben.
Nachlesen: netcup-Firewall einrichten →

Der Server kann keine E-Mails versenden (Port 25 raus geht nicht).

Das ist kein Fehler deiner Policy, sondern netcups Default-Policy „netcup Mail block" – sie sperrt ausgehendes SMTP (Port 25/465/587) als Spam-Schutz. Für einen echten Mailversand musst du dieses netcup-Template am Server deaktivieren.
Nachlesen: netcup-Firewall einrichten →

Der Server läuft zunächst normal und ist nach ein bis zwei Wochen plötzlich offline.

Nutzt der Server DHCP, wird die Lease-Erneuerung von der Whitelist geblockt – die DHCP-Antwort kommt von UDP-Quell-Port 67, den keine Regel erlaubt, und UDP ist zustandslos. Ergänze EINGEHEND · UDP · ACCEPT · Src 67 im Basis-Template. Statisch konfigurierte netcup-VPS (der Standard) sind davon nicht betroffen.
Nachlesen: netcup-Firewall einrichten →

E: Package 'docker-ce' has no installation candidate.

Die Paketquelle aus Schritt 2 fehlt oder ist fehlerhaft – apt kennt den Namen nur aus den Abhängigkeiten der Debian-Pakete, findet aber kein Paket dazu. Prüfe den Inhalt von /etc/apt/sources.list.d/docker.list – dort muss deine Debian-Version (z. B. trixie) stehen – und führe danach erneut sudo apt update aus.
Nachlesen: Docker auf Debian installieren →

permission denied while trying to connect to the docker API at unix:///var/run/docker.sock.

Die docker-Gruppenmitgliedschaft greift noch nicht. SSH-Sitzung beenden und neu verbinden; groups muss danach docker enthalten. Falls nicht, Schritt 4 wiederholen.
Nachlesen: Docker auf Debian installieren →

Konflikte bei der Installation mit bereits vorhandenen Paketen.

Auf dem System ist schon eine andere Docker-Variante installiert (docker.io, podman-docker, …). Alte Pakete zuerst entfernen: sudo apt remove docker.io docker-doc docker-compose podman-docker containerd runc – bestehende Container/Images bleiben dabei erhalten.
Nachlesen: Docker auf Debian installieren →

docker compose meldet docker: unknown command: docker compose.

Das Compose-Plugin fehlt – wahrscheinlich wurde Docker früher anders installiert. sudo apt install docker-compose-plugin nachholen. Achtung: Das alte docker-compose (mit Bindestrich) ist ein anderes, veraltetes Werkzeug.
Nachlesen: Docker auf Debian installieren →

SSH fragt trotz ssh-copy-id weiter nach dem Passwort.

Meist stimmen die Dateirechte auf dem Server nicht: SSH ignoriert authorized_keys, wenn Gruppe oder andere in ~/.ssh schreiben dürfen. Im Log auf dem Server (sudo journalctl -u ssh --since -5min) steht dann Authentication refused: bad ownership or modes for directory /home/koch/.ssh. Repariere mit chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys und prüfe außerdem, dass du dich mit dem richtigen Benutzer verbindest (koch@…, nicht root@…).
Nachlesen: SSH-Zugang absichern →

Permission denied (publickey) – und du kommst gar nicht mehr rein.

Passwort-Login wurde deaktiviert, bevor der Schlüssel funktionierte. Kein Drama: Öffne die VNC-Konsole im netcup SCP, melde dich dort lokal an, setze PasswordAuthentication yes, starte SSH neu und beginne wieder bei Schritt 2.
Nachlesen: SSH-Zugang absichern →

Nach dem Neustart startet der SSH-Dienst nicht mehr.

Syntaxfehler in der sshd_config – ein verschriebenes PasswordAuthentification genügt (deshalb immer sshd -t vor dem Neustart). Über die VNC-Konsole einloggen, dann nennt sudo sshd -t Datei, Zeile und Option: /etc/ssh/sshd_config: line 125: Bad configuration option: PasswordAuthentification gefolgt von /etc/ssh/sshd_config: terminating, 1 bad configuration options.
Nachlesen: SSH-Zugang absichern →

Einstellungen scheinen zu greifen, Passwort-Login geht aber weiterhin.

Eine Datei unter /etc/ssh/sshd_config.d/ (häufig 50-cloud-init.conf) überschreibt deine Werte. sudo sshd -T | grep -i passwordauthentication zeigt die tatsächlich wirksame Einstellung – Treffer in den Zusatzdateien anpassen und SSH neu starten.
Nachlesen: SSH-Zugang absichern →

ssh: connect to host … port 22: Connection timed out.

Meist ist die IP falsch abgetippt oder der Server noch nicht fertig provisioniert. Prüfe die IP in deinem Kundenkonto und ob der Server dort als „online" angezeigt wird. Warte nach der Bestellung ein paar Minuten.
Nachlesen: Erste Schritte mit einem netcup VPS →

Permission denied, please try again beim root-Login.

Falsches Passwort – oft ein Copy-Paste-Problem mit unsichtbaren Leerzeichen am Ende oder ein abweichendes Tastaturlayout. Kopiere das Passwort ohne umgebende Leerzeichen direkt aus den Zugangsdaten.
Nachlesen: Erste Schritte mit einem netcup VPS →

WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!.

Der Server wurde neu installiert und hat einen neuen Host-Key – SSH schlägt zu Recht Alarm. Wenn du selbst neu installiert hast, entferne den alten Eintrag mit ssh-keygen -R DEINE_SERVER_IP und verbinde neu. Hast du nicht neu installiert, geh der Sache nach, bevor du dich verbindest.
Nachlesen: Erste Schritte mit einem netcup VPS →

sudo: command not found als neuer Benutzer.

Auf Minimal-Images fehlt das Paket manchmal. Als root nachinstallieren: apt install sudo. Danach prüfen, dass der Benutzer in der Gruppe ist: groups koch muss sudo enthalten – sonst Schritt 3 wiederholen und einmal ab- und wieder anmelden.
Nachlesen: Erste Schritte mit einem netcup VPS →