@@ -34,9 +34,13 @@ Caddy terminates TLS, so without it Django believes every request is plain HTTP:
|
|||||||
`header_up X-Forwarded-Proto`); don't remove either.
|
`header_up X-Forwarded-Proto`); don't remove either.
|
||||||
|
|
||||||
**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. On one box that is fine. On two, a logo
|
Club logos go to `MEDIA_ROOT` on local disk by default. `compose.yaml` mounts a `media_data`
|
||||||
uploaded to node A is a 404 on node B. Setting `AWS_STORAGE_BUCKET_NAME` switches the
|
volume so that survives a rebuild, and `rosterchief/urls.py` serves `/media/*` itself whenever
|
||||||
default storage to S3 — do it *before* you scale, not during.
|
`AWS_STORAGE_BUCKET_NAME` is unset — Caddy only reverse-proxies, it never serves media on its
|
||||||
|
own, so without that route every logo 404s even on one box. On two boxes local disk stops
|
||||||
|
working regardless: 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
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ services:
|
|||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${WEB_PORT:-8001}:8000"
|
- "127.0.0.1:${WEB_PORT:-8001}:8000"
|
||||||
|
volumes:
|
||||||
|
# Uploaded club logos, while storage is local disk (see rosterchief/urls.py). Without
|
||||||
|
# this, a rebuild or recreate wipes MEDIA_ROOT even though the container itself keeps
|
||||||
|
# running fine in between.
|
||||||
|
- media_data:/app/media
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -53,3 +58,4 @@ services:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
|
media_data:
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ services:
|
|||||||
build: .
|
build: .
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
|
volumes:
|
||||||
|
# Uploaded club logos, while storage is local disk (see rosterchief/urls.py). Without
|
||||||
|
# this, a rebuild or recreate wipes MEDIA_ROOT even though the container itself keeps
|
||||||
|
# running fine in between.
|
||||||
|
- media_data:/app/media
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@@ -70,3 +75,4 @@ volumes:
|
|||||||
pgdata:
|
pgdata:
|
||||||
caddy_data:
|
caddy_data:
|
||||||
caddy_config:
|
caddy_config:
|
||||||
|
media_data:
|
||||||
|
|||||||
@@ -39,5 +39,10 @@ if settings.DEBUG:
|
|||||||
if settings.BROWSER_RELOAD_AVAILABLE:
|
if settings.BROWSER_RELOAD_AVAILABLE:
|
||||||
urlpatterns += [path("__reload__/", include("django_browser_reload.urls"))]
|
urlpatterns += [path("__reload__/", include("django_browser_reload.urls"))]
|
||||||
|
|
||||||
# Club logos are uploads: runserver has to serve MEDIA_ROOT itself.
|
if not settings.AWS_STORAGE_BUCKET_NAME:
|
||||||
|
# Gated on the storage backend, not on DEBUG: local disk is the default until a bucket is
|
||||||
|
# configured (see settings.STORAGES), and Caddy only reverse-proxies — it never serves
|
||||||
|
# /media/* itself — so without this route every uploaded club logo 404s in production too.
|
||||||
|
# Once AWS_STORAGE_BUCKET_NAME is set, club.logo.url points straight at the bucket and this
|
||||||
|
# route is simply never hit.
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|||||||
Reference in New Issue
Block a user