Vielleicht beantworte ich meine eigene Frage: Hier sind einige Notizen aus dem Versuch, Garage mit deinem Runbook zu verwenden:
1. Garage unterstützt keine „Berechtigungstags“ für Dateien (das ist die eigentliche Lücke bei Garage)
Amazon erlaubt es, jede Datei einzeln mit „öffentlich“ oder „privat“ zu versehen. Garage implementiert das nicht.
Discourse versucht standardmäßig, dies zu nutzen. Der heimtückische Teil: Als Discourse sagte „speichere diese Datei, markiere sie als öffentlich“, akzeptierte Garage die Anfrage und ignorierte den Tag leise. Grundlegende Uploads sahen also gut aus, und es wäre erst viel später kaputtgegangen, als jemand zum ersten Mal einen Upload privat machte.
Lösung: Sag Discourse, dass es mit den Tags aufhören soll (eine Einstellung). Cloudflares R2 hat die gleiche Lücke und wird mit der gleichen Lösung behoben. Garage handhabt Berechtigungen auf seine eigene Weise, auf Bucket-Ebene, was alles ist, was wir brauchen.
2. Discourse besteht darauf, Buckets auf eine bestimmte Weise zu benennen (kein Fehler von Garage)
Discourse wird nicht sagen „der Server bei garage, Bucket uploads“. Es besteht auf uploads.garage — der Bucket-Name vorne angeklebt, wie eine Subdomain. Und es gibt keine Option, das auszuschalten.
Nichts in unserem Netzwerk kannte diesen Namen, also konnte Discourse überhaupt keine Verbindung herstellen — die Installation brach中途 ab.
Lösung: Gab Garage diesen Namensstil und registrierte die Namen. Zwei Zeilen Konfiguration.
Dies ist eine bekannte Discourse-Sonderheit, keine von Garage — deshalb ist Oracles Speicher auf der offiziellen „funktioniert nicht“-Liste von Discourse.
3. Bilder brauchten eine öffentliche Webadresse (hat nichts mit Garage zu tun)
Discourse schreibt die Adresse jedes Bildes dauerhaft in den Beitrag. Ohne ihm die öffentliche Adresse im Voraus zu nennen, speicherte es nur interne Adressen, die nur unser Server erreichen kann — also wäre jedes Bild für Besucher für immer kaputt, es sei denn, wir würden jeden Beitrag neu aufbauen.
Lösung: Setze die CDN-Adresse vor dem ersten Upload. Wäre auf Amazon, R2, was auch immer, identisch gewesen.
Hinweis: Jeder dieser Punkte war still. Nichts sagte „nicht unterstützt“. Ein akzeptierte die Anfrage und ignorierte sie, einer sah wie ein Netzwerkfehler aus, einer sah so aus, als würde er gut funktionieren.
Also, nur einige erste Dinge, die ein neuer Benutzer im Hinterkopf behalten sollte, der dies zum ersten Mal versucht, und/oder jemand, der versucht, Michaels Runbook mit Garage zu befolgen.
Hier sind die vollständigen KI-Notizen aus dem, was wir erlebt haben, jetzt da das Forum bereitgestellt wurde:
Discourse auf selbst gehostetem Garage (S3) hinter einem Cloudflare-Tunnel ausführen — Notizen
Garage ist nicht in der Kompatibilitäts-Tabelle „Konfigurieren eines S3-kompatiblen Object-Speicher-Anbieters“ aufgeführt, also hier ein Datenpunkt. Einrichtung: Garage v2.3.0, zwei-Container-Discourse, Debian 13,
einzelner Knoten, Cloudflare-Tunnel statt eines externen nginx. Vorab-Hinweis: Dies ist eine
kleine Bereitstellung ohne Produktivverkehr, also behandle es als „funktioniert“ und nicht als
„kämpferisch getestet im Maßstab der Maker Foren“.
-
Es funktioniert. Verifiziert gegen Discourses eigene Code-Pfade, nicht ein CLI: UploadCreator,
OptimizedImage, ListObjectsV2, HeadObject/GetObject mit byte-exaktem Round-Trip,
multipart, delete, remove_upload, plus BackupRestore::Backuper, das in den backups-
Bucket schreibt, und BackupStore#files / #download_file, das es wieder liest. Lifecycle-Konfiguration
funktioniert, also s3_configure_tombstone_policy wirkt tatsächlich, anstatt
leise ignoriert zu werden. PutBucketCors funktioniert, also ist manuelles CORS in Ordnung.
-
Du musst virtual-host-style-Adressierung konfigurieren, und es wird db:migrate kaputtgehen, wenn du
das nicht tust. Discourse verwandelt Endpunkt http://garage:3900 + Bucket „uploads“ in den Host
uploads.garage:3900, und es gibt keine path-style-Option irgendwo in s3_helper.rb oder
site_settings.yml. Zwei Hälften sind erforderlich: root_domain unter [s3_api] in garage.toml,
UND ein auflösbarer DNS-Name pro Bucket (ein Docker-Netzwerk-Alias pro Bucket, da Docker
DNS keine Wildcards hat). Symptom, wenn du es verpasst, ist ein Aws::Waiters-Fehler bezüglich
getaddrinfo während SiteIconManager.ensure_optimized! — liest sich wie ein Netzwerkfehler, nicht
wie ein Speicherfehler. Jeder neue Bucket braucht einen neuen Namen.
-
Garage implementiert PutObjectAcl / GetObjectAcl nicht, also setze s3_use_acls auf false —
gleich wie R2. Zwei Fallen hier. Erstens, PutObject mit --acl public-read wird leise
AKZEPTIERT und der Header ignoriert, also bestehen grundlegende Upload-Tests und es geht erst später bei
einem secure-upload-Übergang kaputt. Zweitens, DISCOURSE_S3_USE_ACLS ist keine verschattete globale Variable —
es in app.yml zu setzen, tut nichts. Es kam bei meinem ersten Boot als true hoch, obwohl es in
der Umgebungsvariable war. Es muss eine site-setting sein, und es muss nach jedem Rebuild
neu überprüft werden, weil nichts warnt.
-
Wenn du ein CDN voranstellst, zeige es auf Garages WEB-Endpunkt (:3902), nicht den S3-API-
Endpunkt (:3900). Anonyme Lesezugriffe funktionieren nur am Web-Endpunkt; die S3-API gibt korrekt 403 für
nicht authentifizierte Anfragen zurück, also schlägt ein CDN, das auf :3900 zeigt, bei jedem Bild fehl, während deine
Anmeldeinformationen perfekt gültig sind. Du brauchst auch garage bucket website --allow auf dem
uploads-Bucket, und eine root_domain unter [s3_web]. Aktiviere es nur auf uploads — der
backups-Bucket darf niemals anonym lesbar sein.
-
ListObjectVersions gibt NotImplemented auf Garage zurück. Es ist egal: grep nach
list_object_versions / object_versions in s3_helper.rb und file_store/s3_store.rb
ergibt null Treffer. Discourse verwendet keine S3-Objektversionierung; Tombstone-Ablauf ist
Lifecycle-Regeln plus ein Präfix.
-
Testen mit aws-cli oder mc beweist NICHT Kompatibilität. Beide standardisieren auf path-style
Adressierung gegen einen benutzerdefinierten Endpunkt; Discourse macht nur virtual-host-style. Mein CLI-
Durchgang kam mit 15/16 zurück und sah wie ein grünes Licht aus, dann schlug die echte Installation
sofort aufgrund des Adressierungsunterschiedes fehl. Gleicher Speicher, gleiche Anmeldeinformationen, gleiche
Operationen. Wenn du einen unbewiesenen S3-Backend bewertest, treibe echtes Discourse — ein
Wegwerf-Einzel-Container-Instanz reicht aus, und es vor jeglichem Inhalt zu tun, ist der ganze Punkt, da S3 → lokal eine Einbahnstraße ist.
-
Bei einem Cloudflare-Tunnel speziell: Der external-nginx-for-SSL-Abschnitt gilt nicht
(TLS terminiert am Edge, kein certbot, kein eingehender 80/443), aber die real-IP-Ausgangs-
Konfiguration ist dennoch essentiell und arguably sogar mehr. Verwende real_ip_header CF-Connecting-IP
statt X-Forwarded-For — wenn der Tunnel der einzige Ingress-Pfad ist, ist es ein einzelner
unmissverständlicher Edge-Set-Wert. Verifiziere es, indem du von einer bekannten öffentlichen IP postest und prüfst,
was nginx protokolliert hat; ohne es protokolliert Discourse die Docker-Adresse des Connectors für
jede Anfrage und rate-limits das gesamte Internet als einen Client. Was du im Vergleich zum
external-nginx-Ansatz verlierst, ist die Wartungsseite während Rebuilds — cloudflared
kann keine bedienen.
-
Kleine Korrektur zu etwas, das weit verbreitet (falsch) angegeben wird, einschließlich von mir: DISCOURSE_S3_CDN_URL
ist nicht irreversibel in gespeicherte URLs eingearbeitet. upload.url speichert zwar die rohe S3-URL mit
dem internen Host, aber Discourse ersetzt den CDN-Host zur Renderzeit via
Discourse.store.cdn_url / UrlHelper.cook_url. Spät setzen ist wiederherstellbar — die
Kosten sind rake posts:rebake, weil posts.cooked das HTML aus der cook-Zeit cached. Setze es trotzdem
vor dem ersten Upload; panikere nur nicht, wenn du es nicht getan hast.
-
Zwei operative Notizen, die nichts mit Garage zu tun haben. ./launcher echo-t die vollständige docker run-Zeile
einschließlich DISCOURSE_S3_SECRET_ACCESS_KEY im Klartext, also sind Bootstrap-Logs
geheimnisbehaftet — es lohnt sich, Schlüssel nach einer lauten Installation zu rotieren. Und ein einzelner-Knoten-Garage
erfordert immer noch layout assign + layout apply, bevor es irgendetwas speichern wird, mit
replication_factor = 1, was keine Replikation bedeutet, also ist dein Backup-Job die einzige
Dauerhaftigkeit, die du hast.