DEV Community

masato
masato

Posted on

How I burned through Supabase's free egress limit (5GB 10.26GB) with polling and select=*

TL;DR

  • Supabase free plan: 5GB egress/month
  • My project: 10.26GB (205% over limit)
  • Root cause: 5-second polling × select('*') on a table with a 200,000-char text column
  • Fix: generated column flag + select column restriction + visibilitychange polling stop

What happened

My KOBO project (issue-finder) had a collection-queue-form component polling if_jobs every 5 seconds via select('*'). The table includes raw_input_text (up to 200K chars). Through Vercel Functions, every poll was shipping that full column downstream.

5s interval × 200,000 chars × hours of operation = GB gone
Enter fullscreen mode Exit fullscreen mode

Noticed when I checked the Supabase dashboard on 2026-05-31.


Fix 1: Generated column as boolean flag

If I only need to know whether raw input exists, I don't need to ship the text:

ALTER TABLE if_jobs
ADD COLUMN has_raw_input boolean
  GENERATED ALWAYS AS (raw_input_text IS NOT NULL) STORED;
Enter fullscreen mode Exit fullscreen mode

Fix 2: Narrow the select

// Before
const { data } = await supabase.from('if_jobs').select('*')

// After — list view only needs lightweight columns
const { data } = await supabase
  .from('if_jobs')
  .select('id, status, has_raw_input, created_at, updated_at')
Enter fullscreen mode Exit fullscreen mode

Fix 3: Slow down polling + stop on hidden tab

useEffect(() => {
  let interval: ReturnType<typeof setInterval> | null = null
  const start = () => { interval = setInterval(fetch, 30_000) } // 5s → 30s
  const stop = () => { if (interval) clearInterval(interval) }
  document.addEventListener('visibilitychange', () =>
    document.hidden ? stop() : start()
  )
  start()
  return stop
}, [])
Enter fullscreen mode Exit fullscreen mode

When does this happen to you?

  • select('*') on a table with large text/JSON columns
  • Polling interval ≤ 10s
  • Serverless function proxy (Vercel, etc.) — egress counted on every hop

Full writeup (Japanese): https://masatoman.net/articles/supabase-egress-overage-free-plan-2026

Top comments (1)

Collapse
 
vollos profile image
Pon

Nice catch chasing the egress bill down to the actual column, that's the kind of bug that only shows up once you're already over the limit. Since if_jobs holds raw_input_text per job, is that table scoped to the owning user through RLS, or does the fetch happen server-side where that doesn't come into play?