From 33cb3c968093654283c446199c12b17ce9e8e83c Mon Sep 17 00:00:00 2001 From: XShine555 Date: Mon, 20 Jul 2026 17:40:28 +0200 Subject: [PATCH] perf(deploy): cache the YouTube artwork at the nginx edge Thumbnail responses are immutable for a month, so nginx can answer repeats from disk and never wake the web player or Google for them. The cache lives in its own volume, collapses concurrent misses with a lock and serves stale copies while it refreshes; THUMBNAIL_CACHE_SIZE sizes it. --- deploy/.env.prod.example | 2 ++ deploy/compose.prod.yml | 5 +++++ deploy/nginx/templates/default.conf.template | 21 ++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/deploy/.env.prod.example b/deploy/.env.prod.example index 760c7c3..6c603c3 100644 --- a/deploy/.env.prod.example +++ b/deploy/.env.prod.example @@ -60,6 +60,8 @@ AUTH_ALLOW_INSECURE_HTTP=false AUTH_REQUIRE_HTTPS=true # Largest request body nginx accepts (audio uploads go through the web player). MAX_UPLOAD_SIZE=1024m +# Disk nginx gives to the cached YouTube artwork it serves for the web player. +THUMBNAIL_CACHE_SIZE=2g # Extra yt-dlp flags for the Worker. The cookie jar avoids YouTube's bot checks; # drop it here if you do not have one: deploy/secrets/ is mounted at /secrets. YTDLP_ADDITIONAL_ARGUMENTS=--cookies /secrets/youtube_cookies.txt --extractor-retries 1 --socket-timeout 15 -N 4 diff --git a/deploy/compose.prod.yml b/deploy/compose.prod.yml index 1241136..c9ab174 100644 --- a/deploy/compose.prod.yml +++ b/deploy/compose.prod.yml @@ -29,6 +29,7 @@ services: # The filter keeps it from eating nginx's own $host / $remote_addr. MUSIFY_ROOT_DOMAIN: ${ROOT_DOMAIN} MUSIFY_MAX_UPLOAD_SIZE: ${MAX_UPLOAD_SIZE:-1024m} + MUSIFY_THUMBNAIL_CACHE_SIZE: ${THUMBNAIL_CACHE_SIZE:-2g} NGINX_ENVSUBST_FILTER: ^MUSIFY_ ports: - "80:80" @@ -37,4 +38,8 @@ services: - ./nginx/templates:/etc/nginx/templates:ro - ./nginx/snippets:/etc/nginx/snippets:ro - ./nginx/certs:/etc/nginx/certs:ro + - nginx_cache:/var/cache/nginx restart: unless-stopped + +volumes: + nginx_cache: diff --git a/deploy/nginx/templates/default.conf.template b/deploy/nginx/templates/default.conf.template index ed2ac8a..2493e89 100644 --- a/deploy/nginx/templates/default.conf.template +++ b/deploy/nginx/templates/default.conf.template @@ -9,6 +9,12 @@ resolver 127.0.0.11 valid=10s; +# YouTube artwork the web player proxies from Google. Caching it on disk keeps +# repeated searches off both the node process and Google, which answers 429 +# once a client pulls a whole grid of covers at a time. +proxy_cache_path /var/cache/nginx/thumbnails levels=1:2 keys_zone=thumbnails:10m + max_size=${MUSIFY_THUMBNAIL_CACHE_SIZE} inactive=30d use_temp_path=off; + map $http_upgrade $connection_upgrade { default upgrade; '' close; @@ -34,6 +40,21 @@ server { ssl_certificate /etc/nginx/certs/origin.pem; ssl_certificate_key /etc/nginx/certs/origin.key; + location = /api/youtube/thumbnail { + set $upstream http://web-player:3000; + proxy_pass $upstream; + include /etc/nginx/snippets/proxy.conf; + + proxy_cache thumbnails; + proxy_cache_key $uri$is_args$args; + proxy_cache_valid 200 30d; + proxy_cache_valid 400 404 5m; + proxy_cache_lock on; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + proxy_cache_background_update on; + add_header X-Cache-Status $upstream_cache_status; + } + location / { set $upstream http://web-player:3000; proxy_pass $upstream;