Vercel: 504 timeout on serverless functions [2026 fix]

Serverless function exceeds 10s timeout limit or cold start hangs. Increase function timeout, optimize code, or split into smaller functions.

TL;DR

Cause: Your serverless function is taking longer than Vercel's default 10-second timeout (or your configured limit) to respond.

Fix: Increase the maxDuration setting in vercel.json or your function configuration, optimize slow database queries, and implement caching.

---

Real Console Error Messages

``` ERROR: 504 Gateway Timeout Connection timeout after 10000ms ```

``` [FUNCTION_INVOCATION_TIMEOUT] Your serverless function exceeded the maximum execution time of 10 seconds ```

``` Error: Task timed out after 10.00 seconds ```

``` FETCH_TIMEOUT: The request to your function endpoint timed out at Runtime.invokeFunction (internal/serverless:1234) ```

``` 504 Service Unavailable The server did not respond within the specified timeout period. ```

---

Broken Code vs. Fix

Problem 1: Default Timeout Too Low

Broken: ```javascript // pages/api/generate-report.js export default async function handler(req, res) { const data = await fetchLargeDataset(); const processed = await heavyProcessing(data); const pdf = await generatePDF(processed); res.status(200).json({ url: pdf }); } ```

Fixed: ```javascript // pages/api/generate-report.js export default async function handler(req, res) { const data = await fetchLargeDataset(); const processed = await heavyProcessing(data); const pdf = await generatePDF(processed); res.status(200).json({ url: pdf }); }

// vercel.json export const config = { maxDuration: 60 }; ```

Problem 2: Synchronous Processing in Cold Start

Broken: ```typescript // pages/api/process.ts export default async function handler(req: NextApiRequest, res: NextApiResponse) { const users = await db.query('SELECT * FROM users WHERE status = ?', ['active']); users.forEach(user => { expensiveCalculation(user); }); res.json({ processed: users.length }); } ```

Fixed: ```typescript // pages/api/process.ts import { Ratelimit } from '@upstash/ratelimit';

const ratelimit = new Ratelimit({ ... });

export const config = { maxDuration: 30 };

export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { success } = await ratelimit.limit('process'); if (!success) return res.status(429).json({ error: 'Rate limited' }); // Fetch only required columns const users = await db.query( 'SELECT id, name FROM users WHERE status = ? LIMIT 100', ['active'] ); // Queue heavy processing for background job await queue.enqueue({ type: 'batch_process', userIds: users.map(u => u.id) }); res.json({ queued: users.length }); } ```

Problem 3: Missing Connection Pooling

Broken: ```javascript // pages/api/users.js const mysql = require('mysql2/promise');

export default async function handler(req, res) { const connection = await mysql.createConnection({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASS, database: process.env.DB_NAME }); const [rows] = await connection.query('SELECT * FROM users'); await connection.end(); res.json(rows); } ```

Fixed: ```javascript // lib/db.js import mysql from 'mysql2/promise';

let pool;

export async function getPool() { if (!pool) { pool = await mysql.createPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASS, database: process.env.DB_NAME, waitForConnections: true, connectionLimit: 5, queueLimit: 0 }); } return pool; }

// pages/api/users.js import { getPool } from '@/lib/db';

export const config = { maxDuration: 15 };

export default async function handler(req, res) { const pool = await getPool(); const [rows] = await pool.query('SELECT id, name FROM users LIMIT 50'); res.json(rows); } ```

---

Still Broken? Check These Too

1. Cold start delays – Your function dependencies (like heavy npm modules) are too large. Use dynamic imports: const heavy = await import('heavy-lib') to defer loading.

2. External API timeouts cascading – If you're calling third-party APIs inside your function, they're timing out. Implement AbortController with 5s timeout: const controller = new AbortController(); setTimeout(() => controller.abort(), 5000);

3. Memory exhaustion causing slowness – Check CloudWatch/Vercel Analytics. If memory usage spikes near the limit, either upgrade to Pro plan for more RAM or optimize data structures. See [memory optimization guide](/?guide=vercel-memory).

---

Configuration Notes

  • Standard plan: Max 10 seconds (hard limit)
  • Pro plan: Max 60 seconds (adjustable via maxDuration)
  • Enterprise: Custom limits available
  • We cannot confirm if 2026 Vercel versions support custom timeouts beyond 60 seconds – check the [official Vercel serverless function limits documentation](https://vercel.com/docs/functions/serverless-functions#function-timeout).

    ---

    Related Issues

    Check [Cold start optimization strategies](/?guide=vercel-cold-start) and [Database connection pooling for serverless](/?guide=serverless-db-pool).

    ---

    Found a different variation? Drop it in the comments.

    🔥 0d
    LIVE
    ↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising •↑ Resend loved by devs •↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising
    DEVELOPER PAIN RADAR // Loading...

    Developers complain.
    Opportunities appear.

    We track what developers are struggling with today — and what opportunities that creates.

    —
    guides today
    —
    avg happiness
    🔥 Pain
    📖 Guides
    🔭 Explore
    👤 Mine
    🔥 Pain Radar — rage scores today
    ↗ share
    💡 Opportunity Feed — pain = market gap
    📈 Tool Momentum
    all scores →
    📖 Latest Guide
    all guides →
    📖 All Guides
    📊 Tool Scores
    + Submit
    📰 Hacker News
    ➕ Submit a Tool
    ← back