export const maxDuration
Sets the maximum execution time in seconds for server-side logic in a route segment.
export const maxDuration = 5- Added in Next.js v13.4.10
Parameters
| Parameter | Type | Description |
|---|---|---|
$maxDurationoptional | number | Maximum execution time in seconds for server-side logic in the route segment. Deployment platforms read the value from the Next.js build output and apply their own execution limits; there is no framework default. |
Errors
Next.js does not enforce the limit itself — the deployment platform terminates the invocation and surfaces its own timeout error, and platforms reject values above their plan ceiling at build or deploy time.
Changelog
- PHP v13.4.10maxDuration introduced.
maxDuration writes a number into the Next.js build output. Your deployment platform reads it and configures the underlying function's timeout. Next.js does not start a timer of its own, which is the key to understanding both what this export can do and what it cannot.
When to reach for it
A single route does long work — generating a PDF, streaming an LLM response, walking a paginated upstream API — and the platform's default timeout cuts it off. Raising maxDuration on that one segment is far better than raising a global limit, because every other route keeps a short ceiling and a runaway request in an unrelated handler still gets killed quickly.
What trips people up
- The ceiling is not yours to set. The maximum accepted value is a property of the deployment platform and often the billing plan. Setting
maxDuration = 900on a plan that allows 60 fails at build or deploy time rather than silently clamping. - Server Actions inherit the page. An action invoked from a page uses that page's
maxDuration, so the export goes onpage.tsx— not on the'use server'module, which has no segment config. - It does nothing in development.
next devhas no function timeout to configure.
Example
export const maxDuration = 60
export async function GET() {
const rows: string[] = []
for (let page = 1; page <= 40; page++) {
const res = await fetch(
`https://api.example.com/orders?page=${page}&per_page=500`
)
const batch: { id: string; total: number }[] = await res.json()
for (const order of batch) {
rows.push(`${order.id},${order.total}`)
}
}
return new Response(`id,total\n${rows.join('\n')}`, {
headers: {
'Content-Type': 'text/csv',
'Content-Disposition': 'attachment; filename="orders.csv"',
},
})
}When to use it
- Give a CSV export route handler more time than the platform's default timeout.
- Raise the limit on a page whose Server Actions call a slow third-party API.
- Keep one report generator at 60 seconds while every other route stays on the short default.
Frequently asked questions
Why is my route still timing out even though I set maxDuration?
Because Next.js only writes the value into the build output — the deployment platform enforces the timeout, so the value must be within the ceiling your platform and plan allow, and the route must be the segment you actually set it on.
Where do I set maxDuration for a Server Action?
On the page that invokes it: setting maxDuration at the page level changes the default timeout of all Server Actions used on that page, and the 'use server' file itself accepts no segment config.
What is the default value of maxDuration in Next.js?
There is no framework default — the limit is set by your deployment platform, and the export exists only to raise or lower it for a specific segment.
More examples
Raising the ceiling for a page's Server Actions
Shows the placement rule for Server Actions: the limit is read from the page segment, so putting maxDuration in ./actions.ts would have no effect. The action inherits 120 seconds because it is invoked from this page.
// app/import/page.tsx
import { runImport } from './actions'
// Applies to the page render AND to every Server Action
// invoked from this page.
export const maxDuration = 120
export default function ImportPage() {
return (
<form action={runImport}>
<input type="file" name="file" accept=".csv" />
<button type="submit">Start import</button>
</form>
)
}A long-running streaming response
A streamed response keeps the function alive for as long as the upstream keeps writing, so the timeout has to cover the whole stream and not just the time to first byte. 300 is only accepted if the deployment platform and plan allow it.
// app/api/chat/route.ts
export const maxDuration = 300
export async function POST(request: Request) {
const { prompt }: { prompt: string } = await request.json()
const upstream = await fetch('https://api.example.com/completions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt, stream: true }),
})
if (!upstream.body) {
return new Response('No stream', { status: 502 })
}
return new Response(upstream.body, {
headers: { 'Content-Type': 'text/event-stream' },
})
}Scoping the limit to one segment
Only this page gets 90 seconds. Sibling routes keep the platform default, so a hung request elsewhere in the app is still terminated quickly instead of holding an expensive long-lived invocation.
// app/(reports)/heavy/page.tsx
export const maxDuration = 90
export default async function HeavyReportPage() {
const res = await fetch('https://api.example.com/reports/annual')
const report: { sections: { id: string; title: string }[] } =
await res.json()
return (
<article>
{report.sections.map((section) => (
<h2 key={section.id}>{section.title}</h2>
))}
</article>
)
}
Discussion