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
maxDuration)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.