Musa Yılmaz
·7 dk okuma

Vercel Zaman Aşımı: Limitler, maxDuration ve 504 Hatası

vercelnextjsserverlessdeployment

Bir istek takılıyor, sonra tarayıcıya 504 dönüyor ve Vercel'in hata sayfasında FUNCTION_INVOCATION_TIMEOUT yazıyor. İlk refleks süreyi uzatan ayarı aramak. O ayar var ve bazen doğru çözüm de o. Ama çoğu zaman 504 başka bir şey söylüyor: fonksiyon hiç yanıt göndermemiş, dış bir servis çağrısı takılmış ya da bu iş zaten bir HTTP isteğinin içinde yapılacak iş değil.

Bu yazıda limitin bugün tam olarak ne olduğunu, Next.js 16 App Router projesinde nasıl değiştirildiğini ve süreyi artırmakla işi başka yere taşımak arasında nasıl karar verileceğini anlatıyorum. Aşağıdaki rakamlar Ekim 2026'da Vercel dokümanından kontrol edildi; birkaç kez değiştiler, karar vermeden önce tekrar bakın.

Hata ne anlama geliyor

Vercel her fonksiyon çağrısına bir azami süre tanır. Fonksiyon o sürede bitmezse Vercel onu sonlandırır ve FUNCTION_INVOCATION_TIMEOUT koduyla HTTP 504 Gateway Timeout döndürür.

Dokümanda gözden kaçması kolay iki ayrıntı var:

  • Süre, streaming dahil yanıtın tamamını kapsar. Vercel azami süreyi "isteği işlemek ve yanıtı göndermek için geçen süre, stream edilen yanıtlar dahil" diye tanımlıyor. Beş dakika limitli bir route altı dakika stream ederse yayının ortasında kesilir.
  • Hiç yanıt vermeyen fonksiyon da zaman aşımına düşer. Bir kod yolu Response döndürmeyi unutursa (ya da yakalanmamış bir hata handler'ı beklemede bırakırsa) hiçbir şey gönderilmez ve çağrı limit onu öldürene kadar çalışır. 504 beş dakika sonra gelir ve performans sorunu gibi görünür; aslında bir hatadır.

Güncel limitler

Vercel'in 23 Nisan 2025'ten beri yeni projelerde varsayılan olarak açtığı Fluid compute ile Node.js, Bun ve Python runtime'larının limitleri şöyle:

PlanVarsayılanAzamiGenişletilmiş azami
Hobby300 sn300 sn—
Pro300 sn800 sn1800 sn (beta)
Enterprise300 sn800 sn1800 sn (beta)

1800 saniyelik kademe beta aşamasında; proje varsayılanı olarak değil, fonksiyon bazında ayarlanmalı ve yalnızca belirli runtime sürümlerinde çalışıyor.

Edge runtime farklı çalışır: yanıtı göndermeye 25 saniye içinde başlamalı, sonrasında 300 saniyeye kadar stream etmeye devam edebilir.

Hobby için pratik sonuç basit: varsayılan zaten azami değer. Fonksiyonunuz Hobby'de beş dakikada zaman aşımına düşüyorsa hiçbir ayar ona daha fazla süre vermez. Ayarı değil tasarımı değiştirmeniz gereken durum budur.

Projeniz eskiyse ve çok daha kısa bir zaman aşımı görüyorsanız Settings → Functions altında Fluid compute'un açık olup olmadığına bakın. Açık olmayan projeler eski, daha düşük limitlerle çalışır.

App Router'da maxDuration

Next.js'te bunun için bir route segment seçeneği var. Next.js 16 dokümanı maxDuration'ı bir segmentteki sunucu tarafı mantığın saniye cinsinden azami çalışma süresi olarak tanımlıyor; dağıtım platformları bu değeri build çıktısından okuyor. route.ts, page.tsx ya da layout.tsx içinden export edilir:

// app/api/report/route.ts
export const maxDuration = 60;
 
export async function POST(request: Request) {
  const body = await request.json();
  const result = await buildReport(body);
  return Response.json(result);
}

Next.js'e özgü iki nokta:

Server Action'lar sayfanın değerini kullanır. Server Action'ın kendi route dosyası yoktur, yanına maxDuration export edemezsiniz. Doküman bunu sayfa seviyesinde ayarlamayı söylüyor; bu, o sayfada kullanılan tüm Server Action'ların süresini değiştirir.

// app/dashboard/import/page.tsx
export const maxDuration = 120;
 
export default function ImportPage() {
  // aşağıdaki form bir Server Action çağırıyor
  return <ImportForm />;
}

Cache Components bunu kaldırmaz. Next.js 16'da Cache Components açılınca dynamic, dynamicParams, revalidate ve fetchCache segment seçenekleri kalkıyor. maxDuration bu listede yok, iki durumda da çalışmaya devam ediyor.

vercel.json ile ayarlamak

Proje genelinde bir varsayılan için ya da kod seviyesinde seçeneği olmayan framework'lerde vercel.json içindeki functions anahtarını glob ile kullanın:

{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "functions": {
    "src/app/api/**/*.ts": {
      "maxDuration": 30
    }
  }
}

Tuzak yolda. Projeniz src klasörü kullanıyorsa Vercel dokümanına göre desen src/ ile başlamalı; yoksa hiçbir dosyayla eşleşmez ve ayar sessizce uygulanmaz. Vercel ayrıca çıplak **/* deseninin o yol altındaki her şeyi yakaladığını hatırlatıyor; kapsamı daraltmak için dosya uzantısı ekleyin.

Varsayılanı ayarlamanın üçüncü yeri de panel: Settings → Functions altındaki Default Max Duration alanı.

Hangi değer geçerli olur

Aynı fonksiyona birden fazla yerden süre verilirse Vercel şu sırayı uygular (en yüksek öncelik önce):

  1. Fonksiyon kodu (export const maxDuration)
  2. vercel.json
  3. Panel ayarı
  4. Fluid compute varsayılanı

Yani paneldeki değişiklik, birinin route dosyasına yazdığı değeri ezmez. Paneli değiştirdiğiniz hâlde süre değişmiyorsa önce kodda maxDuration arayın.

Artırmadan önce: fonksiyon gerçekten takılı mı?

Vercel'in bu hata için sorun giderme listesi kısa ve maddelerin çoğu "iş çok yavaş" değil:

  • Bir API ya da veritabanı çağrısı limitten uzun sürüyor
  • Fonksiyon bir kod yolunda yanıt döndürmüyor
  • Sonsuz döngü
  • Dış bir servis hata veriyor
  • Yakalanmamış bir istisna

İkinci ve dördüncü durumun ucuz bir çözümü var: dışarı yapılan çağrılara kendi, daha kısa zaman aşımınızı koyun. Böylece takılan bir bağımlılık, beş dakikalık bekleyişin sonunda gelen bir 504 yerine hızlı ve okunabilir bir hataya dönüşür.

export async function GET() {
  try {
    const res = await fetch("https://api.example.com/rates", {
      signal: AbortSignal.timeout(8000),
    });
    if (!res.ok) {
      return Response.json({ error: "upstream error" }, { status: 502 });
    }
    return Response.json(await res.json());
  } catch {
    return Response.json({ error: "upstream timeout" }, { status: 504 });
  }
}

Artık kullanıcı hatayı sekiz saniyede görüyor, loglar hangi bağımlılığın yavaş olduğunu söylüyor ve fonksiyon bir instance'ı boşuna açık tutmuyor. Bu durumda maxDuration'ı artırmak, kullanıcıyı aynı hata için daha uzun bekletmekten başka bir şey yapmaz.

İş isteğin içine ait değilse

İş gerçekten uzunsa (büyük bir dışa aktarım üretmek, yüklenen dosyayı işlemek, yavaş bir üçüncü taraf API'yi defalarca çağırmak) kullanıcının sonuca hemen ihtiyacı olup olmamasına göre üç desen var.

Kullanıcının sonuca ihtiyacı yok: after()

Loglama, analitik, önbellek ısıtma, bildirim e-postası: yanıtın bağlı olmadığı işler. Next.js'te bunun için after() var. Yanıt gönderildikten sonra bir callback çalıştırır; Vercel'de arkasında waitUntil var ve promise tamamlanana kadar çağrıyı canlı tutar.

import { after } from "next/server";
 
export async function POST(request: Request) {
  const order = await saveOrder(await request.json());
 
  after(async () => {
    await sendConfirmationEmail(order.id);
  });
 
  return Response.json({ id: order.id }, { status: 201 });
}

Kullanıcı 201'i hemen alır. Ama Next.js dokümanının süre hakkında söylediğine dikkat: after, route'un platform varsayılanı ya da ayarlanmış azami süresi boyunca çalışır. İşi kullanıcının önünden çeker, zaman aşımından kaçırmaz. Hobby'de after() içindeki on dakikalık bir iş yine beşinci dakikada öldürülür.

Kullanıcının sonuca er geç ihtiyacı var: kuyruğa ver

Dakikalar süren işlerin güvenilir cevabı serverless'tan eski olanı: isteği kabul et, kuyruğa bir iş koy, bir kimlikle birlikte 202 Accepted döndür ve işi istek zaman aşımına bağlı olmayan bir worker yapsın. İstemci bir durum endpoint'ini yoklar ya da iş bitince haberdar edilir.

Next.js'in yanında zaten bir Django backend çalıştırıyorsanız worker büyük ihtimalle Celery'dir ve Next.js tarafının yapacağı tek şey isteği iletmektir:

# views.py (Django)
from django.http import JsonResponse
from .tasks import generate_export
 
def start_export(request):
    task = generate_export.delay(request.user.id)
    return JsonResponse({"task_id": task.id}, status=202)

Next.js fonksiyonu artık istek başına birkaç milisaniyelik iş yapıyor, uzun kısım beş dakikanın duvar olmadığı bir makinede çalışıyor. Celery tarafını, retry ayarı ve görevin neden nesne yerine kimlik alması gerektiği dahil, Django + Celery + Redis: Arka Plan Görevlerini Doğru Yapmak yazısında anlattım.

Tamamen Vercel üzerinde kalmak istiyorsanız Vercel dokümanı, tek bir fonksiyonun limitini aşan ve durup devam etmesi gereken işler için Vercel Workflows'u gösteriyor. Production'da kullanmadım, o yüzden bundan öte anlatmayacağım.

Kullanıcı bekliyor ve sonuç parça parça geliyor: stream et

Yapay zekâ yanıtları ya da uzun listeler için streaming ilk baytları kullanıcıya hızla ulaştırır; bu, 25 saniyelik ilk bayt kuralı olan Edge runtime'da çok önemli. Ayrıca boşta kalan bağlantıların proxy'ler tarafından düşürülmesini önler. Ama limiti uzatmaz. Yukarıda dediğim gibi, stream süresi azami süreye dahil.

Maliyet hakkında

Pro'da fonksiyonlar aktif CPU süresi artı ayrılmış bellek süresi üzerinden ücretlendirilir. I/O beklerken geçen süre (veritabanı sorgusu, dış API) aktif CPU sayılmaz, ama instance çalışmaya devam ettiği için bellek süresi işler. Yüksek bir maxDuration, kontrolden çıkan fonksiyonlar için bedava değil; Vercel'in kendi önerisi de normal çalışmayı karşılayan ve anormal olanı durduran bir değer seçmek. Belirli bir route daha fazlasına ihtiyaç duymuyorsa varsayılanda kalın, duyuyorsa yalnız o route'a ayarlayın.

Özet

  • FUNCTION_INVOCATION_TIMEOUT, çağrının azami süresine ulaştığını söyleyen bir 504'tür. Stream süresi de sayılır.
  • Hobby'de varsayılan da azami de 300 saniye. Artırılacak bir şey yok.
  • Pro'da ihtiyacı olan route ya da sayfaya export const maxDuration yazın (Server Action'lar sayfanın değerini kullanır) veya src klasörünüz varsa src/ ile başlayan bir glob ile vercel.json kullanın. Kod vercel.json'u, vercel.json paneli ezer.
  • Herhangi bir şeyi artırmadan önce eksik yanıt ya da takılan dış çağrı arayın ve dış isteklere kendi kısa zaman aşımınızı koyun.
  • Kullanıcının beklemediği uzun işler, limite sığdığı sürece after()'a gider. Daha uzun olan her şey kuyruğa ve bir worker'a.