From eef4dc7f63c1e9f00712ee885e9011aab94c5cb9 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Fri, 12 Jun 2026 19:15:56 +0000 Subject: [PATCH] =?UTF-8?q?tripit:=20dedicated=20100/1000=20rate-limit=20?= =?UTF-8?q?=E2=80=94=20photo=20grid=20429s=20on=20the=20default=2010/50?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Viktor hit a wall of 429s scrolling the new trip Photos tab: every Immich thumbnail proxies through tripit's /api, so a few-hundred-photo trip is that many parallel GETs from one IP — far past the shared Traefik limiter's average 10 / burst 50. Fourth instance of the parallel-asset pattern (ha-sofia, ActualBudget, noVNC); same cure: dedicated tripit-rate-limit middleware (average 100, burst 1000) + skip_default_rate_limit on the main tripit ingress only. The token-gated calendar/email/slack carve-outs keep the strict default. Co-Authored-By: Claude Fable 5 --- stacks/traefik/modules/traefik/middleware.tf | 25 ++++++++++++++++++++ stacks/tripit/main.tf | 6 +++++ 2 files changed, 31 insertions(+) diff --git a/stacks/traefik/modules/traefik/middleware.tf b/stacks/traefik/modules/traefik/middleware.tf index 0378d0c3..d2749ce0 100644 --- a/stacks/traefik/modules/traefik/middleware.tf +++ b/stacks/traefik/modules/traefik/middleware.tf @@ -319,6 +319,31 @@ resource "kubernetes_manifest" "middleware_actualbudget_rate_limit" { depends_on = [helm_release.traefik] } +# TripIt-specific rate limit. The trip Photos tab proxies every Immich +# thumbnail through tripit's own /api — scrolling a few-hundred-photo trip +# fires that many parallel image GETs from one client IP, and the default +# 10/50 limiter 429s the tail (fourth instance of the parallel-asset +# pattern, after ha-sofia, ActualBudget, and noVNC). Burst must absorb a +# full trip-gallery scroll plus lightbox prefetches. +resource "kubernetes_manifest" "middleware_tripit_rate_limit" { + manifest = { + apiVersion = "traefik.io/v1alpha1" + kind = "Middleware" + metadata = { + name = "tripit-rate-limit" + namespace = kubernetes_namespace.traefik.metadata[0].name + } + spec = { + rateLimit = { + average = 100 + burst = 1000 + } + } + } + + depends_on = [helm_release.traefik] +} + # Compress responses to clients at the entrypoint level (outermost). # Applied at websecure entrypoint so all responses get compressed. # Uses includedContentTypes (whitelist) instead of excludedContentTypes: diff --git a/stacks/tripit/main.tf b/stacks/tripit/main.tf index bbb3cc49..857a7657 100644 --- a/stacks/tripit/main.tf +++ b/stacks/tripit/main.tf @@ -760,6 +760,12 @@ module "ingress" { name = "tripit" port = 8080 tls_secret_name = var.tls_secret_name + # The Photos tab proxies Immich thumbnails through /api — a trip-gallery + # scroll is hundreds of parallel image GETs, far past the default 10/50 + # limiter. Dedicated 100/1000 middleware (stacks/traefik middleware.tf); + # the calendar/email carve-out ingresses below stay on the default. + skip_default_rate_limit = true + extra_middlewares = ["traefik-tripit-rate-limit@kubernetescrd"] } # Calendar feed carve-out for the same host: path /api/calendar served by the