Forse rispondendo alla mia stessa domanda, ecco alcune note derivanti dal tentativo di utilizzare Garage con la tua guida operativa:
1. Garage non supporta i “tag di autorizzazione” sui file (questa è la vera lacuna di Garage)
Amazon ti permette di contrassegnare ogni file come “pubblico” o “privato” individualmente. Garage non implementa questa funzionalità.
Discourse cerca di usarlo di default. L’aspetto subdolo: quando Discourse diceva “salva questo file, contrassegnalo come pubblico”, Garage accettava la richiesta e ignorava silenziosamente il tag. Quindi i caricamenti base sembravano funzionare correttamente, e si sarebbe rotto solo molto più tardi, la prima volta che qualcuno avesse reso un caricamento privato.
Soluzione: dire a Discourse di smettere di usare i tag (un’impostazione). Cloudflare R2 ha la stessa lacuna e riceve la stessa soluzione. Garage gestisce le autorizzazioni in modo autonomo, a livello di bucket, che è tutto ciò di cui abbiamo bisogno.
2. Discourse insiste nel nominare i bucket in un modo specifico (non è un difetto di Garage)
Discourse non dirà “il server su garage, bucket uploads”. Insiste su uploads.garage — il nome del bucket incollato all’inizio, come un sottodominio. E non c’è alcuna opzione per disabilitarlo.
Nessuna parte della nostra rete conosceva quel nome, quindi Discourse non poteva connettersi affatto — l’installazione è fallita a metà.
Soluzione: ho dato a Garage quello stile di denominazione e ho registrato i nomi. Due righe di configurazione.
Questa è una peculiarità nota di Discourse, non di Garage — è il motivo per cui lo storage di Oracle è nella lista ufficiale “non funzionante” di Discourse.
3. Le immagini avevano bisogno di un indirizzo web pubblico (nulla a che fare con Garage)
Discourse scrive l’indirizzo di ogni immagine nel post in modo permanente. Senza dirgli l’indirizzo pubblico in anticipo, ha salvato indirizzi interni raggiungibili solo dal nostro server — quindi ogni immagine sarebbe risultata rotta per i visitatori, per sempre, a meno che non ricostruissimo ogni post.
Soluzione: impostare l’indirizzo CDN prima del primo caricamento. Sarebbe stato identico su Amazon, R2, qualsiasi altro servizio.
Nota: ognuno di questi problemi è stato silenzioso. Nulla ha detto “non supportato”. Uno ha accettato la richiesta e l’ha ignorata, uno sembrava un errore di rete, uno sembrava funzionare perfettamente.
Quindi, solo alcune cose iniziali da tenere a mente per un nuovo utente che prova questo per la prima volta, e/o per qualcuno che cerca di seguire la guida operativa di Michael con Garage.
Ecco le note AI complete su ciò che abbiamo sperimentato ora che il forum è stato distribuito:
Esecuzione di Discourse su Garage self-hosted (S3) dietro un tunnel Cloudflare — note
Garage non è presente nella tabella di compatibilità “Configurare un provider di storage oggetti compatibile con S3”, quindi ecco un dato. Configurazione: Garage v2.3.0, Discourse in due container, Debian 13,
nodo singolo, tunnel Cloudflare invece di un nginx esterno. Premessa: questa è una
piccola distribuzione senza traffico di produzione, quindi trattatela come “funziona” e non come “testata
alla scala di Maker Forums”.
-
Funziona. Verificato contro i percorsi di codice di Discourse, non tramite CLI: UploadCreator,
OptimizedImage, ListObjectsV2, HeadObject/GetObject con round trip byte-esatto,
multipart, delete, remove_upload, più BackupRestore::Backuper che scrive nel bucket
dei backup e BackupStore#files / #download_file che lo legge. La configurazione del ciclo di vita
funziona, quindi s3_configure_tombstone_policy ha effettivamente effetto invece di essere
ignorata silenziosamente. PutBucketCors funziona, quindi CORS manuale va bene.
-
Devi configurare l’indirizzamento in stile virtual-host, e romperà db:migrate se non lo fai.
Discourse trasforma l’endpoint http://garage:3900 + bucket “uploads” nell’host
uploads.garage:3900, e non c’è alcuna opzione path-style in s3_helper.rb o
site_settings.yml. Sono richieste due parti: root_domain sotto [s3_api] in garage.toml,
E un nome DNS risolvibile per bucket (un alias di rete Docker per bucket, poiché il DNS
Docker non ha wildcard). Il sintomo se lo perdi è un errore Aws::Waiters su
getaddrinfo durante SiteIconManager.ensure_optimized! — sembra un guasto di rete, non
di storage. Ogni nuovo bucket ha bisogno di un nuovo nome.
-
Garage non implementa PutObjectAcl / GetObjectAcl, quindi imposta s3_use_acls su false —
come R2. Due trappole qui. Prima, PutObject con --acl public-read è
ACCETTATO silenziosamente e l’header è ignorato, quindi i test di caricamento base passano e si rompe solo più tardi
su una transizione a upload sicuro. Seconda, DISCOURSE_S3_USE_ACLS non è una variabile globale nascosta —
metterla in app.yml non fa nulla. È risultata true al mio primo avvio nonostante fosse
nell’ambiente. Deve essere un’impostazione di sito, e deve essere ricontrollata dopo ogni rebuild
perché nulla ti avvisa.
-
Se metti un CDN davanti, puntalo all’endpoint WEB di Garage (:3902), non all’API S3
(:3900). Le letture anonime funzionano solo sull’endpoint web; l’API S3 restituisce correttamente 403
alle richieste non autenticate, quindi un CDN puntato a :3900 fallisce su ogni immagine mentre le tue
credenziali sono perfettamente valide. Hai anche bisogno di garage bucket website --allow sul
bucket uploads, e di un root_domain sotto [s3_web]. Attivalo solo su uploads — il
bucket dei backup non deve mai essere leggibile anonimamente.
-
ListObjectVersions restituisce NotImplemented su Garage. Non importa: cercando
s3_helper.rb e file_store/s3_store.rb per list_object_versions / object_versions
non si trovano occorrenze. Discourse non usa la versioning degli oggetti S3; la scadenza dei tombstone è
tramite regole di ciclo di vita più un prefisso.
-
Testare con aws-cli o mc NON prova la compatibilità. Entrambi usano di default
l’indirizzamento path-style contro un endpoint personalizzato; Discourse usa solo virtual-host style. Il mio passaggio
CLI è tornato 15/16 e sembrava un via libera, poi l’installazione reale è fallita
immediatamente sulla differenza di indirizzamento. Stesso storage, stesse credenziali, stesse
operazioni. Se stai valutando un backend S3 non provato, fai girare un vero Discourse — una
istanza single-container usa e getta è sufficiente, e farlo prima che esista contenuto è il punto, poiché S3 → locale è a senso unico.
-
Specificamente su un tunnel Cloudflare: la sezione external-nginx-for-SSL non si applica
(TLS termina al bordo, nessun certbot, nessuna porta 80/443 in ingresso), ma la configurazione real-IP outlet
è ancora essenziale e forse più importante. Usa real_ip_header CF-Connecting-IP
invece di X-Forwarded-For — quando il tunnel è l’unico percorso di ingresso è un singolo
valore edge-set inequivocabile. Verificalo pubblicando da un IP pubblico noto e controllando
cosa ha registrato nginx; senza di esso Discourse registra l’indirizzo Docker del connettore per
ogni richiesta e limita la velocità di tutto l’Internet come un solo cliente. Ciò che perdi rispetto
all’approccio external-nginx è la pagina di manutenzione durante i rebuild — cloudflared
non può servirla.
-
Minore correzione a qualcosa ampiamente (male)detto, incluso da me: DISCOURSE_S3_CDN_URL
non è incorporato in modo irreversibile negli URL memorizzati. upload.url memorizza l’URL S3 grezzo con
l’host interno, ma Discourse sostituisce l’host CDN al momento del rendering tramite
Discourse.store.cdn_url / UrlHelper.cook_url. Impostarlo in ritardo è recuperabile — il
costo è rake posts:rebake, perché posts.cooked memorizza in cache l’HTML dal momento della cottura. Impostalo comunque
prima del primo caricamento; ma non andare nel panico se non l’hai fatto.
-
Due note operative non correlate a Garage. ./launcher eco la riga docker run completa
inclusa DISCOURSE_S3_SECRET_ACCESS_KEY in testo piano, quindi i log di bootstrap contengono segreti — vale la pena ruotare le chiavi dopo un’installazione rumorosa. E un Garage single-node
richiede comunque layout assign + layout apply prima di memorizzare qualcosa, con
replication_factor = 1 che significa nessuna replica, quindi il tuo lavoro di backup è l’unica
durabilità che hai.