Skip to main content
RapidDev - Software Development Agency
supabase-tutorial

How to Reduce Cold Start Time in Supabase Edge Functions

Supabase Edge Functions run on a Deno-based runtime and experience cold starts when a function has not been invoked recently. Reduce cold start time by minimizing the function bundle size, lazy-loading heavy dependencies, avoiding top-level await for slow operations, and keeping functions warm with scheduled pings. Functions under 5MB with minimal imports typically cold start in under 200ms.

What you'll learn

  • What causes cold starts in Supabase Edge Functions and how to measure them
  • How to minimize bundle size by reducing and lazy-loading dependencies
  • How to keep functions warm with scheduled pings using pg_cron
  • Best practices for structuring Edge Functions for fast startup
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read10-15 minSupabase (all plans), Deno runtime, supabase CLIMarch 2026RapidDev Engineering Team
TL;DR

Supabase Edge Functions run on a Deno-based runtime and experience cold starts when a function has not been invoked recently. Reduce cold start time by minimizing the function bundle size, lazy-loading heavy dependencies, avoiding top-level await for slow operations, and keeping functions warm with scheduled pings. Functions under 5MB with minimal imports typically cold start in under 200ms.

Reducing Cold Start Times in Supabase Edge Functions

Cold starts occur when a Supabase Edge Function has not been invoked recently and needs to be initialized from scratch. The runtime must load the function code, initialize dependencies, and set up the execution environment. This tutorial shows you practical techniques to minimize cold start impact, from code optimization to warm-keeping strategies.

Prerequisites

  • A Supabase project with at least one Edge Function deployed
  • The Supabase CLI installed and linked to your project
  • Basic understanding of Deno and TypeScript
  • Access to the Supabase Dashboard for monitoring function logs

Step-by-step guide

1

Understand what causes cold starts

A cold start happens when your Edge Function has not been invoked for a period of time and the runtime instance has been shut down. The next request must spin up a new instance, which involves loading your function code, resolving and importing dependencies, and executing any top-level initialization code. The main factors affecting cold start duration are: bundle size (larger functions take longer to load), number and size of imports (each dependency adds initialization time), and top-level code execution (code that runs at import time before the request handler). Typical cold starts for small functions are 50-200ms. Large functions with many dependencies can take 500ms-2s.

Expected result: You understand the three main factors that contribute to cold start time.

2

Minimize bundle size by reducing imports

Every import adds to the code that must be loaded on cold start. Import only what you need — avoid importing entire libraries when you only use one function. For npm packages used via the npm: specifier in Deno, the entire package is bundled. Prefer smaller, focused packages over large utility libraries. For the Supabase client specifically, import from the npm specifier rather than a CDN URL for better caching.

typescript
1// BAD: Importing entire lodash (70KB+ bundled)
2import _ from 'npm:lodash'
3const result = _.pick(data, ['name', 'email'])
4
5// GOOD: Import only what you need
6import pick from 'npm:lodash.pick'
7const result = pick(data, ['name', 'email'])
8
9// BEST: Use native JavaScript instead of lodash
10const { name, email } = data
11const result = { name, email }

Expected result: Your function imports are minimized, using only the specific modules needed.

3

Lazy-load heavy dependencies inside the request handler

Move heavy imports inside the request handler function so they are loaded only when needed, not at initialization time. Top-level imports run during cold start initialization, adding to the startup time even if the specific code path is not used for every request. Use dynamic import() for dependencies that are only needed in certain code paths.

typescript
1import { corsHeaders } from '../_shared/cors.ts'
2
3// Keep the Supabase client at top level (it's lightweight)
4import { createClient } from 'npm:@supabase/supabase-js@2'
5
6Deno.serve(async (req) => {
7 if (req.method === 'OPTIONS') {
8 return new Response('ok', { headers: corsHeaders })
9 }
10
11 const { action } = await req.json()
12
13 // Lazy-load heavy dependencies only when needed
14 if (action === 'generate-pdf') {
15 const { default: PDFDocument } = await import('npm:pdfkit')
16 // Use PDFDocument...
17 }
18
19 if (action === 'send-email') {
20 const { Resend } = await import('npm:resend')
21 // Use Resend...
22 }
23
24 return new Response(JSON.stringify({ ok: true }), {
25 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
26 })
27})

Expected result: Heavy dependencies are loaded on demand instead of at initialization, reducing cold start time for requests that don't use them.

4

Avoid top-level await for slow operations

Top-level await blocks function initialization until the awaited operation completes. This means any database queries, API calls, or file reads at the top level add directly to cold start time. Move these operations inside the request handler or use lazy initialization patterns that run on the first request instead of at module load time.

typescript
1// BAD: Top-level await adds to cold start time
2const supabase = createClient(
3 Deno.env.get('SUPABASE_URL')!,
4 Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
5)
6const { data: config } = await supabase.from('config').select('*').single()
7
8// GOOD: Create client at top level but fetch data lazily
9const supabase = createClient(
10 Deno.env.get('SUPABASE_URL')!,
11 Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
12)
13
14let cachedConfig: any = null
15
16async function getConfig() {
17 if (!cachedConfig) {
18 const { data } = await supabase.from('config').select('*').single()
19 cachedConfig = data
20 }
21 return cachedConfig
22}
23
24Deno.serve(async (req) => {
25 const config = await getConfig()
26 // Use config...
27})

Expected result: Slow async operations are deferred to the first request instead of blocking cold start initialization.

5

Keep functions warm with scheduled pings

To prevent cold starts entirely for critical functions, set up a scheduled ping that invokes the function periodically. Use the pg_cron extension in Supabase to schedule a lightweight HTTP request to your function every few minutes. The function can respond immediately to these pings, keeping the runtime instance warm. This approach costs minimal resources but ensures your function is always ready for real user requests.

typescript
1-- Enable pg_cron extension (run once in SQL Editor)
2create extension if not exists pg_cron;
3
4-- Schedule a ping every 5 minutes to keep the function warm
5select cron.schedule(
6 'keep-function-warm',
7 '*/5 * * * *',
8 $$
9 select
10 net.http_post(
11 url := 'https://<project-ref>.supabase.co/functions/v1/my-function',
12 headers := jsonb_build_object(
13 'Authorization', 'Bearer ' || current_setting('app.settings.service_role_key'),
14 'Content-Type', 'application/json'
15 ),
16 body := '{"ping": true}'::jsonb
17 );
18 $$
19);

Expected result: The function is invoked every 5 minutes, preventing the runtime from shutting down and eliminating cold starts.

6

Measure cold start impact

To quantify your cold start optimization, measure the response time of your function after a period of inactivity versus after a recent invocation. Use the Edge Function logs in the Dashboard to see execution times. You can also add timing instrumentation in your function code. Compare the first request after deployment (guaranteed cold start) with subsequent requests to see the difference.

typescript
1Deno.serve(async (req) => {
2 const start = performance.now()
3
4 // Your function logic here
5 const result = { message: 'Hello!' }
6
7 const duration = performance.now() - start
8 console.log(`Request processed in ${duration.toFixed(2)}ms`)
9
10 return new Response(JSON.stringify(result), {
11 headers: { 'Content-Type': 'application/json' },
12 })
13})

Expected result: You can measure and compare cold start vs warm request times to verify that your optimizations are effective.

Complete working example

optimized-edge-function.ts
1// supabase/functions/optimized/index.ts
2// An Edge Function optimized for minimal cold start time
3
4import { corsHeaders } from '../_shared/cors.ts'
5import { createClient } from 'npm:@supabase/supabase-js@2'
6
7// Lightweight top-level initialization (synchronous, no await)
8const supabase = createClient(
9 Deno.env.get('SUPABASE_URL')!,
10 Deno.env.get('SUPABASE_ANON_KEY')!
11)
12
13// Lazy-initialized cache for config data
14let configCache: Record<string, string> | null = null
15
16async function getConfig() {
17 if (!configCache) {
18 const { data } = await supabase
19 .from('config')
20 .select('key, value')
21 configCache = Object.fromEntries(
22 (data || []).map((row) => [row.key, row.value])
23 )
24 }
25 return configCache
26}
27
28Deno.serve(async (req) => {
29 // Handle CORS preflight
30 if (req.method === 'OPTIONS') {
31 return new Response('ok', { headers: corsHeaders })
32 }
33
34 const start = performance.now()
35
36 try {
37 const body = await req.json()
38
39 // Quick return for warm-keeping pings
40 if (body.ping) {
41 return new Response('{"pong":true}', {
42 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
43 })
44 }
45
46 // Load config lazily on first real request
47 const config = await getConfig()
48
49 // Lazy-load heavy deps only when needed
50 let result: any
51 if (body.action === 'process') {
52 // Main logic using lightweight operations
53 result = { processed: true, config }
54 }
55
56 const duration = performance.now() - start
57 console.log(`Processed in ${duration.toFixed(2)}ms`)
58
59 return new Response(JSON.stringify(result), {
60 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
61 })
62 } catch (err) {
63 return new Response(JSON.stringify({ error: err.message }), {
64 status: 400,
65 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
66 })
67 }
68})

Common mistakes when reducing Cold Start Time in Supabase Edge Functions

Why it's a problem: Importing large libraries at the top level that are only used in rare code paths

How to avoid: Use dynamic import() inside the specific code path that needs the library. This prevents the import from adding to cold start time for requests that don't need it.

Why it's a problem: Using top-level await for database queries or API calls that block initialization

How to avoid: Move async operations inside the request handler or use a lazy initialization pattern with caching. Only synchronous, lightweight code should run at the top level.

Why it's a problem: Deploying functions with --no-verify-jwt for warm-keeping pings without adding auth in the ping request

How to avoid: Keep JWT verification enabled and include a valid Authorization header in the ping request. Disabling JWT verification for warm-keeping creates a security vulnerability.

Best practices

  • Keep function bundle size under 5MB for cold starts under 200ms
  • Import only the specific modules you need instead of entire packages
  • Use dynamic import() for heavy dependencies in conditional code paths
  • Avoid top-level await — defer slow initialization to the first request
  • Cache configuration and reusable data between requests using module-level variables
  • Set up pg_cron warm-keeping pings for latency-critical functions
  • Add a quick-return path for ping requests to minimize warm-keeping cost
  • Monitor cold start times in Dashboard logs and optimize the slowest functions first

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

My Supabase Edge Function has a 1.5 second cold start time. It imports several npm packages and makes a database query at the top level. Show me how to optimize it for faster cold starts by lazy-loading dependencies, deferring database queries, and setting up a pg_cron warm-keeping schedule.

Supabase Prompt

Optimize a Supabase Edge Function for minimal cold start time. Move heavy imports to dynamic import(), use lazy initialization for database config, add a quick-return path for warm-keeping pings, and set up a pg_cron job to ping the function every 5 minutes.

Frequently asked questions

What is a typical cold start time for Supabase Edge Functions?

Small functions (under 5MB with few imports) typically cold start in 50-200ms. Larger functions with many npm dependencies can take 500ms-2 seconds. The Deno runtime caches compiled code, so subsequent cold starts after the first deployment are usually faster.

How long before an idle function gets shut down?

Supabase does not publish the exact idle timeout, but functions are typically shut down after a few minutes of inactivity. The exact timing may vary by plan and server load. Setting up a 5-minute ping schedule reliably prevents cold starts.

Does the function size limit affect cold starts?

Yes, the maximum bundled function size is 20MB. Larger bundles take longer to load on cold start. Aim to keep your bundled size under 5MB for optimal cold start performance.

Can I pre-warm functions after deployment?

Yes, after deploying with supabase functions deploy, immediately invoke the function once to trigger a cold start. This pre-warms the function so the first real user request is fast. Combine with pg_cron pings to keep it warm.

Do warm-keeping pings count toward function invocation limits?

Yes, each ping is a function invocation and counts toward your plan's limits. At one ping every 5 minutes, that is 288 invocations per day per function — well within most plan limits.

Is there a difference between cold starts on free vs paid plans?

The cold start mechanism is the same across all plans. However, paid plans may have higher concurrency limits, meaning functions stay warm longer under consistent load. The runtime and initialization process is identical.

Can RapidDev help optimize my Supabase Edge Functions for performance?

Yes, RapidDev can audit your Edge Functions for cold start bottlenecks, optimize imports and initialization patterns, and set up warm-keeping infrastructure for latency-critical functions.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.