Installer für Traefik+Crowdsec Stack + Netbird, Mailcow, Forgejo
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-03 12:45:47 +02:00
0001-forgejo-v16-upgrade.patch Forgejo-Upgrade: v16-Pruefungen nach Release Notes, Option auch in install-forgejo.sh 2026-10-02 22:12:28 +02:00
admin_password WOPI-Positivliste um die Docker-Gateways, OPcache anhebbar 2026-09-19 18:46:36 +02:00
collabora-templatefelder.patch Forgejo-Upgrade: v16-Pruefungen nach Release Notes, Option auch in install-forgejo.sh 2026-10-02 22:12:28 +02:00
install-collabora.sh Forgejo-Upgrade, Collabora-Rahmen- und Jail-Pruefung, Nextcloud-Cron-Netz 2026-09-20 18:21:17 +00:00
install-crowdsec-manager.sh CrowdSec Manager: wahlweise nur im NetBird-Netz erreichbar 2026-08-29 11:32:15 +02:00
install-forgejo.sh Forgejo-Upgrade: v16-Pruefungen nach Release Notes, Option auch in install-forgejo.sh 2026-10-02 22:12:28 +02:00
install-mailcow.sh Mailcow: DNS vollstaendig pruefen und Hostnamen nachtragbar machen 2026-08-10 21:15:27 +02:00
install-netbird-behind-traefik.sh Fehlersuche: auf die richtige Traefik-Logquelle verweisen 2026-08-10 20:19:44 +02:00
install-nextcloud.sh Forgejo-Upgrade, Collabora-Rahmen- und Jail-Pruefung, Nextcloud-Cron-Netz 2026-09-20 18:21:17 +00:00
install-traefik-crowdsec-debian13.sh Remote-Ausfuehrung: Eingaben vom Terminal lesen, README ergaenzt 2026-08-10 18:40:30 +02:00
memory.md Collabora: mount_jail_tree abschalten statt CAP_SYS_ADMIN zu verlangen 2026-09-19 19:10:05 +02:00
op.yml WOPI-Positivliste um die Docker-Gateways, OPcache anhebbar 2026-09-19 18:46:36 +02:00
README.md README: Rybbit-Modul und Tracking-Proxy dokumentiert 2026-10-03 12:45:47 +02:00
setup.sh Rybbit als zehntes Modul: Web-Analytics hinter Traefik, Tracking-Proxy über die Website-Domain gegen Adblocker 2026-10-03 12:45:47 +02:00

Traefik + CrowdSec + NetBird + Forgejo + Mailcow + Nextcloud + Collabora + CrowdSec Manager + Documenso + Rybbit

Interaktive Installer für einen Debian-13-Server: Reverse Proxy mit Traefik und CrowdSec, dazu wahlweise NetBird, Forgejo, Mailcow, Nextcloud, Collabora Online, die CrowdSec-Weboberfläche, Documenso und Rybbit – alle hinter demselben Traefik.

Die Scripts lesen die Umgebung selbst aus, statt sie zu erfragen: Docker-Netz, Subnetz, Traefik-IP, Entrypoints und Certresolver kommen aus traefik.yml und der Docker-API. Sie werden angezeigt und mit einer einzigen Rückfrage übernommen; nur wenn etwas nicht erkennbar ist, wird nachgefragt. Vor jeder Änderung an fremden Dateien entsteht ein Backup.

Schnellstart

# Alles auf einmal: Docker, Traefik-CrowdSec, NetBird, Forgejo, Mailcow, Nextcloud, Collabora, CrowdSec Manager, Documenso, Rybbit
bash <(curl -fsSL https://git.keil-schick.de/viktor/public-vserver-installer/raw/branch/main/setup.sh)

Als root ausführen (oder sudo -i davor). Ohne Argumente erscheint ein Menü.

Warum bash <(curl …) und nicht curl … | bash? Beides funktioniert. Bei curl … | bash liest bash das Script selbst über stdin; die Scripts erkennen das und holen die Eingaben dann direkt vom Terminal. Läuft gar kein Terminal (Cron, CI), brechen sie mit einem Hinweis ab, statt stumm Vorgaben zu übernehmen.

Alternativ klonen:

git clone https://git.keil-schick.de/viktor/public-vserver-installer.git /opt/setup
cd /opt/setup && sudo ./setup.sh

Ein einzelnes Script lässt sich genauso direkt ziehen:

bash <(curl -fsSL https://git.keil-schick.de/viktor/public-vserver-installer/raw/branch/main/install-mailcow.sh)

Die raw-URLs funktionieren nur, solange das Repo öffentlich ist. Bei einem privaten Repo entweder über SSH klonen (git clone ssh://git@git.keil-schick.de/viktor/public-vserver-installer.git) oder curl einen Token mitgeben: curl -fsSL -H "Authorization: token <PAT>" …

Aufrufe

sudo ./setup.sh                     # interaktives Menü
sudo ./setup.sh --all               # alle zehn Module nacheinander
sudo ./setup.sh --docker --traefik  # nur Basis und Reverse Proxy
sudo ./setup.sh --netbird           # NetBird zu bestehendem Traefik
sudo ./setup.sh --forgejo           # Forgejo zu bestehendem Traefik
sudo ./setup.sh --mailcow           # Mailcow zu bestehendem Traefik
sudo ./setup.sh --nextcloud         # Nextcloud zu bestehendem Traefik
sudo ./setup.sh --collabora         # Collabora Online zu bestehendem Traefik
sudo ./setup.sh --crowdsec-ui       # CrowdSec-Weboberfläche zu bestehendem Traefik
sudo ./setup.sh --documenso         # Documenso zu bestehendem Traefik
sudo ./setup.sh --rybbit            # Rybbit (Web-Analytics) zu bestehendem Traefik

sudo ./setup.sh --status            # was ist installiert und läuft
sudo ./setup.sh --update            # Images aktualisieren (optional mit Backup)
sudo ./setup.sh --backup            # Konfiguration und Volumes sichern
sudo ./setup.sh --remove            # Module entfernen (mit Sicherheitsabfrage)
sudo ./setup.sh --forgejo-user      # Benutzer in Forgejo anlegen
sudo ./setup.sh --forgejo-ssh       # Git-over-SSH prüfen und reparieren
sudo ./setup.sh --forgejo-mailer    # SMTP von Forgejo nachträglich ändern
sudo ./setup.sh --mailcow-hosts     # DNS prüfen und Mailcow-Hostnamen nachtragen
sudo ./setup.sh --nextcloud-tune    # Optimierungen einer bestehenden Nextcloud
sudo ./setup.sh --nextcloud-domain  # Domain einer bestehenden Nextcloud ändern
sudo ./setup.sh --nextcloud-appapi  # AppAPI-Daemon (HaRP) neu registrieren
sudo ./setup.sh --collabora-domains # erlaubte Clouds ändern (Aliasgruppen und CSP)
sudo ./setup.sh --collabora-nextcloud # Collabora in bestehender Nextcloud eintragen
sudo ./setup.sh --crowdsec-ui-auth  # Zugang zur CrowdSec-Oberfläche ändern
sudo ./setup.sh --documenso-admin   # Benutzer in Documenso zum Administrator machen
sudo ./setup.sh --documenso-cert    # Signaturzertifikat von Documenso austauschen
sudo ./setup.sh --rybbit-proxy      # Tracking-Skript über die Domain der Website ausliefern

Module lassen sich frei kombinieren; Reihenfolge und Dubletten korrigiert das Script selbst.

Module

Modul Was passiert
docker Docker Engine + Compose v2 aus dem offiziellen Repo, rsyslog, optional IPv6 und UFW
traefik traefik-crowdsec-stack klonen, konfigurieren, CrowdSec-Firewall-Bouncer einrichten
netbird NetBird self-hosted (Management, Signal, Relay, STUN) plus Reverse-Proxy-Dienst
forgejo Forgejo + PostgreSQL, Git-over-SSH auf Port 22 über den Host-sshd
mailcow Mailcow-dockerized, Zertifikate über certdumper aus Traefiks ACME-Speicher
nextcloud Nextcloud + MariaDB + Redis, Cron-Container, optional HaRP für externe Apps
collabora Collabora Online (CODE) mit eigener Traefik-Middleware, Anbindung an Nextcloud
crowdsec-ui CrowdSec Manager (hhftechnology), wahlweise öffentlich mit Basic Auth oder nur im NetBird-Netz
documenso Documenso + PostgreSQL, Signaturzertifikat wird erzeugt oder übernommen, SMTP wie bei Forgejo
rybbit Rybbit (Web-Analytics) + ClickHouse + PostgreSQL + Redis, ohne Caddy; optional Tracking-Proxy gegen Adblocker

Reihenfolge: docker → traefik → alles andere. Die acht Dienstmodule setzen einen laufenden Traefik voraus; collabora bindet sich an eine vorhandene Nextcloud an, wenn eine läuft.

Einzelscripts

setup.sh enthält alle Module. Wer nur eines braucht, kann auch das passende Einzelscript nehmen – inhaltlich identisch:

sudo ./install-traefik-crowdsec-debian13.sh
sudo ./install-netbird-behind-traefik.sh
sudo ./install-forgejo.sh            # --mailer für nachträgliche SMTP-Änderung
sudo ./install-mailcow.sh
sudo ./install-nextcloud.sh          # --tune, --domain, --appapi für spätere Änderungen
sudo ./install-collabora.sh          # --domains, --nextcloud für spätere Änderungen
sudo ./install-crowdsec-manager.sh   # --auth zum Ändern des Zugangs (nur im Traefik-Modus)

Voraussetzungen

  • Debian 13 (Trixie), 64 Bit – anderes läuft meist auch, wird aber abgefragt
  • Root-Zugriff
  • Öffentliche IP, Ports 80 und 443 erreichbar
  • Für jeden Dienst ein DNS-Eintrag auf den Server; NetBird und Mailcow brauchen zusätzliche Einträge, die die Scripts am Ende auflisten

Was die Scripts an bestehenden Dateien ändern

Jede Änderung an Dateien außerhalb des eigenen Verzeichnisses wird vorher als .bak.<Zeitstempel> gesichert.

  • NetBird ergänzt in traefik.yml allowACMEByPass: true und setzt die respondingTimeouts auf 0 (nötig für langlebige gRPC-Streams), und legt in dynamic_conf einen TCP-serversTransport für PROXY-Protocol v2 an.
  • Forgejo legt einen Host-Benutzer git an, dazu ein sshd-Drop-in unter /etc/ssh/sshd_config.d/, drei Shims unter /usr/local/bin und eine sudo-Regel. Vor dem Reload prüft es die Konfiguration mit sshd -t und nimmt sie bei Fehlern zurück.
  • Mailcow erweitert acquis.yaml und COLLECTIONS von CrowdSec und legt bei Bedarf blockierende Host-MTAs still (siehe unten).
  • Nextcloud legt auf Wunsch eine AppSec-Whitelist an und trägt den Container als CrowdSec-Logquelle in acquis.yaml ein. Mit HaRP kommt ein serversTransport für die ExApps in dynamic_conf dazu.
  • Collabora legt in dynamic_conf eine eigene Middleware-Kette an (http.middlewares.collabora.yml). Die gemeinsame default-security-headers bleibt unangetastet.
  • Rybbit legt mit --rybbit-proxy in dynamic_conf die Datei http.routers.rybbit-proxy.yml an (Router und Rewrites für die Tracking-Proxys). Sie entsteht jedes Mal neu aus /opt/containers/rybbit/proxy-hosts.conf.

Stolpersteine

Debian belegt Port 25. Im Standardumfang läuft exim4 auf 127.0.0.1:25, womit Docker 0.0.0.0:25 nicht mehr binden kann. Das Mailcow-Modul erkennt das, zeigt den belegenden Dienst und bietet an, ihn zu stoppen oder zu entfernen.

tls_resolver und NetBird vertragen sich schlecht. Das für den NetBird-Proxy nötige allowACMEByPass nimmt Traefiks internem ACME-Router den Vorrang; Resolver mit tlsChallenge können dadurch keine Zertifikate mehr ausstellen. Eigene Dienste dann auf einen httpChallenge-Resolver umstellen.

Forgejo kann kein XOAUTH2. Für Microsoft 365 bleibt nur Basic Auth, das Microsoft Ende Dezember 2026 für bestehende Tenants standardmäßig abschaltet. Dauerhaft tragfähig sind der Connector-Relay über Port 25 oder – falls installiert – die lokale Mailcow.

Der Git-SSH-Shim muss dort liegen, wo Forgejo ihn erwartet — und das ist argv[0], nicht das echte Binary. Forgejo bildet den Pfad für die authorized_keys-Zeile mit exec.LookPath(os.Args[0]), also aus argv[0] des laufenden Prozesses, über den PATH des Containers aufgelöst. Im Image ist /usr/local/bin/gitea ein Symlink auf /app/gitea/gitea: readlink -f liefert das Ziel, in die Key-Zeile schreibt Forgejo aber den Symlink. Liegt der Shim nur am Ziel, glückt die Anmeldung und erst der Klon scheitert:

bash: line 1: /usr/local/bin/gitea: No such file or directory

Das Script liest deshalb argv[0] aus dem Container und belegt zusätzlich alle plausiblen Orte (/usr/local/bin/forgejo, /usr/local/bin/gitea, /app/gitea/gitea) — echte Programme werden dabei nicht überschrieben. --forgejo-ssh prüft jeden dieser Orte einzeln und repariert.

--config gehört vor keys, nicht dahinter. Es ist ein globales Flag der Wurzel. Steht es hinter dem Unterbefehl, startet Forgejo ohne Konfiguration und bricht mit Unable to load config file ab – sshd protokolliert nur AuthorizedKeysCommand … failed, status 1, der Client sieht Permission denied (publickey). Über den Wrapper /usr/local/bin/forgejo fällt das nicht auf, weil der -c selbst ergänzt; beim echten Binary schon. Das Script probiert die Form deshalb einmal aus, statt sie anzunehmen.

Die Schlüsselabfrage läuft in einer nackten Umgebung. sshd ruft AuthorizedKeysCommand und den forced command ohne geerbte Umgebung auf — kein PATH, keine Variablen. Ein über PATH gesuchtes docker oder sudo ist dort nicht garantiert auffindbar; das Ergebnis wäre ein stummes Permission denied (publickey), während aus einer Root-Shell heraus alles zu funktionieren scheint. Die erzeugten Hilfsscripts setzen deshalb selbst einen PATH und rufen docker und sudo absolut auf. Die Selbsttests laufen aus demselben Grund unter env -i, also so, wie sshd es tut.

Fehler der Schlüsselabfrage sind sichtbar. /usr/local/bin/forgejo-authorized-keys schreibt jede Fehlermeldung ins Syslog, weil sshd nur AuthorizedKeysCommand … failed meldet und der Client bloß Permission denied (publickey) sieht:

journalctl -t forgejo-authorized-keys -n 20
env -i /usr/local/bin/forgejo-authorized-keys git ssh-ed25519 <base64-key>   # von Hand

Ein unbekannter Schlüssel endet mit Rückgabewert 1, nicht 0. forgejo keys meldet dann etwas wie public key does not exist — das ist Normalbetrieb und sieht im sshd-Log identisch aus wie ein echter Defekt (AuthorizedKeysCommand … failed, status 1). Unterschieden wird deshalb an der Meldung: Hinweise auf Konfiguration, Container, Docker-Socket oder falsche Flags sind Störungen, alles andere heißt „Forgejo kennt den Schlüssel nicht". Prüfungen, die nur den Rückgabewert ansehen, schlagen bei jeder frischen Installation Fehlalarm.

Ein Schlüssel gehört immer zu genau einer Instanz. Steht er im Profil einer anderen Forgejo, ist die Abweisung korrekt — der Fingerprint aus ssh-keygen -lf <datei>.pub muss in dieser Instanz unter SSH-Schlüssel stehen.

Ein fehlender DNS-Eintrag kippt das ganze Zertifikat. Der Traefik-Router von Mailcow deckt sieben Namen ab (mail, autodiscover, autoconfig, mta-sts, imap, pop3, smtp). Let's Encrypt lehnt die Anfrage für alle ab, sobald einer davon nicht auflöst. Das Modul prüft deshalb vorher und bietet an, fehlende Namen zu deaktivieren; nachtragen später mit --mailcow-hosts.

AppSec blockiert WebDAV, nicht die Weboberfläche. Ohne Whitelist läuft Nextcloud im Browser normal, aber Desktop- und Handy-Clients synchronisieren nicht mehr – CRS-Regel 911100 lässt die WebDAV-Methoden PROPFIND, REPORT und MOVE nicht durch. Das Nextcloud-Modul legt die Whitelist deshalb an.

Apache begrenzt Uploads unabhängig von PHP. APACHE_BODY_LIMIT steht standardmäßig auf 1 GiB und schneidet größere Uploads ab, egal was upload_max_filesize erlaubt. Das Modul setzt den Wert auf 0 (unbegrenzt) und schreibt alle Grenzen als Umgebungsvariablen – eine .user.ini im app-Verzeichnis wäre beim nächsten Update wieder weg.

AppAPI: --harp entscheidet über den Pfad. Ohne das Kennzeichen fragt Nextcloud http://<host>/v1.44/_ping ab (alter Docker Socket Proxy), mit ihm http://<host>/exapps/app_api/v1.44/_ping. Passt beides nicht zusammen, antwortet HAProxy mit 403 Request forbidden by administrative rules – das ist die Ablehnung durch die Pfadregeln, kein falsches Passwort (das gäbe 401). Das Modul registriert den Daemon deshalb selbst per occ … --harp. --nextcloud-appapi registriert nachträglich neu und rüstet HaRP nach, wenn es noch fehlt.

Der Docker Socket Proxy fällt weg. Die AppAPI nennt ihn seit Nextcloud 34 „deprecated, scheduled for removal in Nextcloud 35". Das Modul installiert deshalb HaRP: kein privileged mehr, dafür ein Traefik-Router auf https://<cloud>/exapps/ und Port 8782 am Host für den FRP-Tunnel der ExApps.

WOPI-Anfragen kommen nicht von der Container-IP. Collabora ruft die Cloud über deren öffentliche Adresse auf; auf einem einzelnen Server landet das per NAT wieder im eigenen Haus, und Nextcloud sieht als Absender das Gateway der Brücke, über die das Paket hereinkommt – nicht die Adresse des Collabora-Containers. Im Log steht dann:

WOPI request denied from 172.31.64.1 as it does not match the configured ranges

Das Modul trägt deshalb alle Docker-Gateways in wopi_allowlist ein, nicht nur das Proxy-Subnetz. Nachträglich: --collabora-nextcloud.

Halbes IPv6 auf dem Host sieht aus wie ein DNS-Problem. Steht in /etc/docker/daemon.json "ipv6": true ohne ip6tables und ohne fixed-cidr-v6, bekommen Container IPv6-Adressen, aber es gibt kein NAT dafür. Nextcloud löst AAAA auf, verbindet ins Leere und meldet:

LocalServerException: No DNS record found for apps.nextcloud.com

Der Hinweis zeigt in die falsche Richtung – die Auflösung funktioniert, der Weg fehlt. Das Modul prüft deshalb nach dem Start beide Familien aus dem Container heraus und bietet an, IPv6 dort abzuschalten (sysctls: net.ipv6.conf.all.disable_ipv6=1), dann bleibt nur noch der IPv4-Weg. Nachträglich prüft und behebt das --nextcloud-tune. Sauberer, aber invasiver ist die Alternative am Host: ip6tables und fixed-cidr-v6 nachtragen — das betrifft dann alle Container.

OPcache: 128 MB reichen Nextcloud nicht. Das Image gibt diesen Wert vor, Nextcloud mahnt „OPcache-Puffer ist fast voll" an. Das Modul setzt PHP_OPCACHE_MEMORY_CONSUMPTION=256; bei bestehenden Installationen hebt --nextcloud-tune den Wert an und legt den Container neu an.

Nextcloud springt nur eine Hauptversion. Steht der Tag auf latest und liegen zwei Versionen dazwischen, bleibt die Instanz im Wartungsmodus stehen. --update warnt davor; sicherer ist eine feste Version in der .env.

Der Editor bleibt leer, wenn frame-ancestors fehlt. Ob Nextcloud Collabora einbetten darf, entscheidet der Browser anhand der Header von Collabora – nicht denen der Cloud. Der Stack setzt in default-security-headers aber frame-ancestors 'self' und X-Frame-Options: SAMEORIGIN und hängt das über default@file an jeden Dienst. Das Modul legt deshalb eine eigene Kette collabora@file an: gleiche Härtung, passende frame-ancestors, kein X-Frame-Options. Anhängen an default@file hilft nicht – bei verketteten Middlewares gewinnt auf dem Rückweg die äußere.

CODE 26.04 braucht mount_jail_tree=false oder CAP_SYS_ADMIN. Ohne eines von beidem kommt im Log im Sekundentakt:

Failed to exec coolmount [/usr/bin/coolmount]. The helper needs CAP_SYS_ADMIN

Das Modul setzt --o:mount_jail_tree=false in extra_params – Collabora kopiert dann statt zu mounten, was den Start etwas verlangsamt und sonst gleichwertig ist. cap_add: SYS_ADMIN wäre die Alternative, kommt aber privilegierten Rechten sehr nahe.

Der Entrypoint-Override braucht eine Shell, die es geben muss. Die Anleitung setzt entrypoint: ['/bin/bash', '-c'], um vor dem Start coolconfig generate-proof-key laufen zu lassen. Fehlt /bin/bash im Image, startet der Container gar nicht – und docker compose logs bleibt leer, weil nie ein Prozess lief:

exec: "/bin/bash": stat /bin/bash: no such file or directory

Seit CODE 26.04 ist das Image distroless: keine Shell, kein env, kein coreutils; Entrypoint ist direkt /usr/bin/coolwsd --use-env-vars …, und einen Healthcheck (coolwsd --probe) bringt es selbst mit. Das Modul probiert die Shell deshalb am Image aus und lässt Entrypoint und Healthcheck unangetastet, wenn keine da ist — den Proof-Key legt Collabora dann beim ersten Start selbst an. Die Konfiguration über Umgebungsvariablen (aliasgroup1, server_name, username, extra_params) funktioniert unverändert.

Aliasgruppe und CSP sind zwei Hälften derselben Einstellung – aber nicht buchstabengleich. Fehlt die Cloud-Adresse in der Aliasgruppe, lehnt Collabora ab; fehlt sie in der CSP, bleibt der Rahmen leer, ohne dass im Log etwas auffällt. --collabora-domains ändert deshalb immer beide zusammen. Der Unterschied: die Aliasgruppe braucht https://cloud.example.com:443, die CSP darf den Port nicht enthalten – er ist bei https ohnehin der Standard, und Safari gleicht eine Quelle mit explizitem Port nicht gegen eine Adresse ohne Port ab:

Refused to load https://office.example.com/browser/…/cool.html
because it does not appear in the frame-ancestors directive

Compose frisst das $ im Passwort-Hash. In einem Docker-Label ist jedes $ der Beginn einer Variablen – $apr1$… oder $2y$05$… kommt sonst verstümmelt bei Traefik an und die Anmeldung schlägt fehl, ohne dass im Log etwas Auffälliges steht. Das crowdsec-ui-Modul verdoppelt deshalb jedes $ und prüft nach dem Schreiben mit docker compose config nach, wie der Wert tatsächlich ankommt.

Die CrowdSec-Oberfläche darf schreiben. Sie kann Sperren aufheben und Regeln ändern und hat den Docker-Socket eingehängt – ein Zugang dorthin wiegt schwerer als er aussieht. Das Modul fragt deshalb, wie sie erreichbar sein soll:

  • öffentlich über Traefik – eigene Subdomain, Zertifikat, Basic Auth davor (--crowdsec-ui-auth ändert die Zugangsdaten)
  • nur im NetBird-Netz – keine Domain, kein Zertifikat, kein veröffentlichter Port. Der Container bekommt eine feste Adresse im Proxy-Netz, ein NetBird-Client auf dem Host gibt genau diese als /32-Route frei. Damit entfällt allerdings die Basic Auth – sie ist eine Traefik-Middleware; wer im VPN ist, kommt hinein. Einschränken über NetBirds Zugriffsrichtlinien.

Der Server ist nicht automatisch Teil seines eigenen VPN. Das netbird-Modul installiert nur den Server-Stack; netbird-proxy steht vor der Steuerebene und leitet keinen VPN-Verkehr weiter. Für den VPN-Zugang installiert das Modul zusätzlich den NetBird-Client auf dem Host und schaltet die IPv4-Weiterleitung ein – ohne sie nimmt der Host Pakete an, reicht sie aber nicht an die Container weiter.

Veröffentlichte Docker-Ports umgehen UFW. Docker schreibt seine Regeln in die DOCKER-USER-Kette, die vor UFW greift – „an 0.0.0.0 binden und mit UFW absichern" schützt also nicht. Deshalb im VPN-Modus gar kein Port, sondern eine Route auf eine feste Adresse.

Port 25 ausgehend wird von vielen Hostern blockiert und muss freigeschaltet werden, sonst kann Mailcow nicht zustellen.

Mailcow-Updates brauchen das mitgelieferte ./update.sh; ein reines docker compose pull überspringt die Migrationen.

Documenso ohne Passwort auf dem Zertifikat signiert nicht. Eine .p12-Datei ohne Passwort quittiert die Anwendung mit Failed to get private key bags. Das Modul erzeugt deshalb immer eines und legt es als SIGNING_PASSPHRASE in der .env ab. Die Datei gehört UID 1001 und hat 400 – der Container läuft nicht als root und kann sie sonst nicht lesen.

Documenso erzeugt die .p12 mit 3DES statt der AES-256-Vorgabe von OpenSSL 3 (-legacy -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES). Documenso liest das Zertifikat mit node-forge, und 3DES ist dort der verlässliche Weg; -certpbe RC2 – die eigentliche Vorgabe von -legacy – fällt damit ebenfalls weg, sodass sich die Datei auch ohne Legacy-Provider wieder öffnen lässt.

Documenso ruft sich für Hintergrundjobs selbst auf. Deshalb steht NEXT_PRIVATE_INTERNAL_WEBAPP_URL auf http://localhost:3000: über die öffentliche Adresse liefe der Aufruf durch Traefik und CrowdSec wieder herein, und Mails wie Signaturläufe bleiben hängen.

Administrator wird man in Documenso nur in der Datenbank. Das erste Konto entsteht über die normale Registrierung, die Rolle setzt danach sudo ./setup.sh --documenso-admin. Anschließend DISABLE_SIGNUP=true in der .env und docker compose up -d.

Rybbits Tracking-Proxy läuft komplett in Traefik. Pro Website ein Router auf Host(<website>) und genau die Endpunkte des Skripts (script.js, replay.js, metrics.js, track, identify, Session-Replay, Tracking-Konfiguration, Feature-Flags), umgeschrieben auf /api/... und direkt an rybbit-api@docker. Login und Dashboard-API bleiben auf der Rybbit-Domain. Zwei Formen: ein Pfad auf der Website (kunde.de/x7k2/script.js, die Website muss auf diesem Traefik laufen) oder eine eigene Subdomain (t.kunde.de/script.js, geht auch für extern gehostete Websites). Traefik reicht alle Browser-Header und die Besucher-IP weiter – mit zu wenigen Headern sortiert Rybbits Bot-Erkennung die Events stillschweigend aus. In Rybbit je Website unter Site Settings → Privacy & Security „First-Party Proxy“ einschalten. Den Pfad neutral wählen: /analytics, /stats oder /rybbit stehen in Filterlisten.

Rybbits Container heißen rybbit-*, nicht wie im offiziellen Compose backend, postgres, redis. Das Backend hängt auch im Proxy-Netz, und dort lösen gleichnamige Dienste anderer Stacks sonst womöglich auf den falschen Container auf. ClickHouse bekommt ein mem_limit (Vorgabe: ein Viertel des RAM, 2–8 GB) – ohne Grenze nimmt es sich bis zu 80 % des gesamten Speichers.

Rybbits Analysedaten sind nicht im --backup. Gesichert werden Konfiguration, Tracking-Proxys und ein Dump der PostgreSQL-Datenbank; das ClickHouse-Volume rybbit_clickhouse-data braucht ein eigenes Volume-Backup.

Nach der Installation

Jedes Modul legt in seinem Verzeichnis eine INSTALL-INFO.txt (chmod 600) mit Zugangsdaten, DNS-Einträgen und den nützlichsten Befehlen ab:

/opt/containers/traefik-crowdsec-stack/INSTALL-INFO.txt
/opt/containers/netbird/INSTALL-INFO.txt
/opt/containers/forgejo/INSTALL-INFO.txt
/opt/containers/mailcow/INSTALL-INFO.txt
/opt/containers/nextcloud/INSTALL-INFO.txt
/opt/containers/collabora/INSTALL-INFO.txt
/opt/containers/crowdsec-manager/INSTALL-INFO.txt
/opt/containers/documenso/INSTALL-INFO.txt
/opt/containers/rybbit/INSTALL-INFO.txt

Quellen

Die Scripts setzen im Wesentlichen diese Anleitungen um, mit Anpassungen für Debian 13 und den gemeinsamen Traefik: