From 9a27723b52f55b5043b05d74cc5666df3925d2b6 Mon Sep 17 00:00:00 2001 From: jamey Date: Mon, 2 Mar 2026 09:49:53 +0000 Subject: [PATCH] persist image cache on fly volume across deploys Image variants were written to the ephemeral release directory and wiped on every deploy, causing 500 errors with 50s timeouts as browsers waited for images that could never be served. - Point image_cache_dir at /data/image_cache in prod - Add Plug.Static to serve from the persistent volume - Exclude /image_cache/ from broken URL tracking Co-Authored-By: Claude Opus 4.6 --- config/prod.exs | 3 +++ lib/berrypod_web/endpoint.ex | 9 +++++++++ lib/berrypod_web/plugs/broken_url_tracker.ex | 1 + 3 files changed, 13 insertions(+) diff --git a/config/prod.exs b/config/prod.exs index 050640b..9c2f673 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -19,5 +19,8 @@ config :logger, level: :info # Structured JSON logs for production (machine-parseable by fly logs, journalctl, Loki, etc.) config :logger, :default_handler, formatter: {LoggerJSON.Formatters.Basic, []} +# Persistent image cache on the Fly volume (survives deploys) +config :berrypod, :image_cache_dir, "/data/image_cache" + # Runtime production configuration, including reading # of environment variables, is done on config/runtime.exs. diff --git a/lib/berrypod_web/endpoint.ex b/lib/berrypod_web/endpoint.ex index bbf97c0..4f5199d 100644 --- a/lib/berrypod_web/endpoint.ex +++ b/lib/berrypod_web/endpoint.ex @@ -16,6 +16,15 @@ defmodule BerrypodWeb.Endpoint do websocket: [connect_info: [session: @session_options]], longpoll: [connect_info: [session: @session_options]] + # In prod, image variants live on the persistent volume (/data/image_cache) + # rather than inside the ephemeral release directory. + if image_cache_dir = Application.compile_env(:berrypod, :image_cache_dir) do + plug Plug.Static, + at: "/image_cache", + from: image_cache_dir, + cache_control_for_etags: "public, max-age=31536000, immutable" + end + # Serve at "/" the static files from "priv/static" directory. # gzip only in prod — avoids stale .gz files from mix assets.deploy # shadowing freshly-built dev assets. diff --git a/lib/berrypod_web/plugs/broken_url_tracker.ex b/lib/berrypod_web/plugs/broken_url_tracker.ex index 978015c..ae65b97 100644 --- a/lib/berrypod_web/plugs/broken_url_tracker.ex +++ b/lib/berrypod_web/plugs/broken_url_tracker.ex @@ -34,6 +34,7 @@ defmodule BerrypodWeb.Plugs.BrokenUrlTracker do defp static_path?(path) do String.starts_with?(path, "/assets/") or String.starts_with?(path, "/images/") or + String.starts_with?(path, "/image_cache/") or String.starts_with?(path, "/favicon") end end