Serve club logos directly from Caddy instead of Django
Every image request was round-tripping through a gunicorn worker for what is just a static file on disk. Caddy now serves /media/* straight off the shared media_data volume (mounted read-only) and only falls through to Django for anything else — Django's own /media/* route stays as a fallback for compose.behind-proxy.yaml and runserver, where there is no bundled Caddy container.
This commit is contained in:
@@ -35,12 +35,14 @@ Caddy terminates TLS, so without it Django believes every request is plain HTTP:
|
|||||||
|
|
||||||
**4. Uploads must move to object storage before the second app server.**
|
**4. Uploads must move to object storage before the second app server.**
|
||||||
Club logos go to `MEDIA_ROOT` on local disk by default. `compose.yaml` mounts a `media_data`
|
Club logos go to `MEDIA_ROOT` on local disk by default. `compose.yaml` mounts a `media_data`
|
||||||
volume so that survives a rebuild, and `rosterchief/urls.py` serves `/media/*` itself whenever
|
volume, shared read-write with `web` and read-only with `caddy`, so uploads both survive a
|
||||||
`AWS_STORAGE_BUCKET_NAME` is unset — Caddy only reverse-proxies, it never serves media on its
|
rebuild and get served by Caddy directly (`handle_path /media/*` in the Caddyfile) rather than
|
||||||
own, so without that route every logo 404s even on one box. On two boxes local disk stops
|
round-tripping through a gunicorn worker. `rosterchief/urls.py` still serves `/media/*` itself
|
||||||
working regardless: a logo uploaded to node A is still a 404 on node B, since nothing shares
|
as a fallback whenever `AWS_STORAGE_BUCKET_NAME` is unset — needed for `compose.behind-proxy.yaml`
|
||||||
the volume between them. Setting `AWS_STORAGE_BUCKET_NAME` switches the default storage to S3
|
(no bundled Caddy there) and for `runserver`. On two boxes local disk stops working regardless
|
||||||
— do it *before* you scale, not during.
|
of any of this: a logo uploaded to node A is still a 404 on node B, since nothing shares the
|
||||||
|
volume between them. Setting `AWS_STORAGE_BUCKET_NAME` switches the default storage to S3 — do
|
||||||
|
it *before* you scale, not during.
|
||||||
|
|
||||||
**5. PDF invoices need native libraries.**
|
**5. PDF invoices need native libraries.**
|
||||||
WeasyPrint binds to pango/cairo. The image installs them; a bare-metal deploy would need
|
WeasyPrint binds to pango/cairo. The image installs them; a bare-metal deploy would need
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ services:
|
|||||||
- ./deploy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
- ./deploy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
- caddy_data:/data
|
- caddy_data:/data
|
||||||
- caddy_config:/config
|
- caddy_config:/config
|
||||||
|
# Read-only: Caddy serves club logos straight off disk instead of round-tripping every
|
||||||
|
# image request through a gunicorn worker. Same volume `web` writes uploads into.
|
||||||
|
- media_data:/srv/media:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- web
|
- web
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,15 @@
|
|||||||
|
|
||||||
encode zstd gzip
|
encode zstd gzip
|
||||||
|
|
||||||
|
# Club logos, served straight off the shared volume — no gunicorn worker involved. Only
|
||||||
|
# matters while storage is local disk; once AWS_STORAGE_BUCKET_NAME is set, club.logo.url
|
||||||
|
# points at the bucket directly and this block simply never matches. A missing file 404s
|
||||||
|
# here exactly as django.views.static.serve would, so there is no need to fall through.
|
||||||
|
handle_path /media/* {
|
||||||
|
root * /srv/media
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
|
||||||
# X-Forwarded-Proto is what SECURE_PROXY_SSL_HEADER reads. Without it Django believes every
|
# X-Forwarded-Proto is what SECURE_PROXY_SSL_HEADER reads. Without it Django believes every
|
||||||
# request is plain HTTP: request.is_secure() goes false, WebAuthn disagrees with the browser
|
# request is plain HTTP: request.is_secure() goes false, WebAuthn disagrees with the browser
|
||||||
# about the origin, and the SSL redirect becomes a loop.
|
# about the origin, and the SSL redirect becomes a loop.
|
||||||
|
|||||||
Reference in New Issue
Block a user