# Vercel Function Bills by the Second, Dies at 10

> Duration meters plus hard timeouts: long tasks die at 10s while the bill counts every ms. Background the work or leave. Both paths inside.

Source: https://boringpush.com/blog/vercel-function-duration-timeout-fix

## The export died at second ten, billed to second nine
A customer clicked "export yearly report." Eleven seconds of honest work. At second ten Vercel killed it. The customer got a 504. The invoice got nine billed seconds of compute that produced nothing.

Splitting the export into chunks (complexity!), adding progress polling (more functions, more meters!), watching the dashboard turn one button into eleven metered invocations. The timeout didn't just kill the task — it multiplied the billing while doing it.

Vercel function duration timeout is the only meter that charges you for failing: pay per millisecond, die by hard clock, retry on your own dime.

## Duration billing vs timeout kills
| Work shape | Duration cost | Timeout fate |
|---|---|---|
| 200ms API read | Pennies, fine | Never hit |
| 5s report build | Metered seconds | Hobby kills at ~10s |
| 30s CSV export | Real money | Dead everywhere serverless |
| 5min video job | Absurd if allowed | Never allowed (use queue) |
| Retry after timeout | Billed twice | Customer waits twice |

Three details make it sting twice:

1. **Billed failure is the business model.** Killed functions still consumed GB-hours — the meter has no "did it work" input. Every timeout is pure margin for the platform: compute sold, value undelivered, retry purchased separately. Failure monetizes better than success.
2. **Timeouts push work into more meters.** Chunking splits one function into polling chains and status endpoints. Beating the clock costs architecture plus multiplied billing.
3. **Limits vary by plan opaquely.** Hobby ~10s, Pro ~60s (verify current — fluid compute and plan changes move these), with duration pricing layered per GB-hour on top. Two dimensions (clock + meter) shifting per tier means capacity planning needs a spreadsheet and a lawyer. Or a VPS with neither.

The $900 post metered it. This one buries work that outgrows it.

## Background it or unclock it
Split by duration: seconds stay serverless-shaped, minutes go elsewhere.

**Step 1 — Know your clock before designing.**

```bash
# check YOUR limits today (dashboard → Functions → settings, verify current):
# Hobby: ~10s timeout | Pro: ~60s | duration billed per GB-hour past allowance
# design rule: anything over 50% of your clock belongs elsewhere, not optimized
```

Designing to 9.5s on a 10s clock is engineering on a cliff edge — one slow dependency Tuesday and exports die in prod. The 50% rule keeps headroom honest: under half the clock, optimize freely; over half, relocate. Clocks are constraints to respect, not targets to approach.

**Step 2 — Background the long work (stay, restructured).**

```js // pattern: accept → queue → poll (each step under the clock)
// POST /api/export → validate, enqueue job id, return 202 in <200ms
// worker (cron/queue): process minutes-long, write result to storage
// GET /api/export/:id → status or download link (fast reads only)
```

Accept-fast plus poll-slow keeps every invocation milliseconds-cheap while minutes-long work happens where no clock ticks. Queues (or $0 cron workers on VPS) absorb the duration the meter punished. Complexity cost is real (three endpoints where one lived) — justified exactly when the work exceeds half the clock, never before.

**Step 3 — Unclock it entirely on the VPS.**

```bash
# same export, $5 box: PM2 worker, no timeout, no per-ms meter
# 5-minute CSV: runs 5 minutes, costs $0 marginal, customer gets file
# tradeoff: you own retries (try/catch + dead-letter table, 20 lines)
```

No clock, no GB-hour meter, no chunking architecture — long work runs as long as it takes, billed at $0 marginal on flat hardware. The threshold is honest: sub-second APIs stay serverless-shaped anywhere (even on VPS they run fast); minute-plus work belongs where time is free. Move the work to fit the clock, or move off clocks entirely.

Which is the point — buttons that finish, whatever they take.

## The export, three ways
| Yearly-export button | Naive serverless | Chunked serverless | VPS worker |
|---|---|---|---|
| Customer result | 504 at 10s | File after 11 polls | File, one wait |
| Billed compute | 9s wasted + retries | 11 invocations metered | $0 marginal |
| Code complexity | 1 function | 3 endpoints + state | 1 worker + table |
| Failure mode | Timeout + charge | Poll fatigue + charge | Retry locally, free |

The part nobody advertises: VPS workers need owned retry logic (dead letters, idempotency, alerting on stuck jobs) that serverless queues bundle. BoringPush prices twenty lines of retry code against per-ms meters plus timeout architecture: owned simplicity beats rented complexity for every workload with a human waiting. Clocks suit machines; humans suit workers without them.

## FAQ

### Does vercel function duration timeout apply to edge functions too?
Edge has its own stricter clocks (verify current — historically tighter than serverless) with CPU-time (not wall-time) metering, a different trap: waiting on APIs burns wall clock free but CPU spikes bill. Know which clock governs each route (edge vs serverless vs fluid), design under half of it, relocate past it. Three runtimes, three clocks, one 50% rule.

### How do streaming responses interact with timeouts?
Streaming keeps connections open (better UX: partial content flows) but the function clock still ticks wall-time underneath — streams delay the kill, they don't cancel it. Long streams need the background pattern (stream progress events, not the result). Streaming is perceived performance; timeouts measure actual duration. Don't confuse the audience's comfort with the clock's patience.

### Should I just raise concurrency instead of backgrounding?
Concurrency multiplies throughput, never duration: fifty parallel 9s functions still die at 10s individually. Parallelize independent chunks (map phase) AND background the aggregation (reduce phase off-clock). Concurrency answers "many," backgrounding answers "long" — different questions, complementary tools, often both in the same export.

### What about Vercel's fluid compute / maxDuration settings?
Worth configuring (maxDuration buys headroom per plan limits, fluid smooths cold starts) — verify current caps, as these move with plans. Treat settings as optimization within the clock, not escape from it: doubled timeouts halve the panic, they don't remove the meter or the kill. Configure first (free headroom), relocate second (structural fix).

### How do VPS workers handle retries without a queue service?
Try/catch with exponential backoff inline, dead-letter rows in SQLite (failed jobs table, inspected weekly), idempotency keys per job (replays safe), Telegram scream on stuck-over-1h. Twenty lines replaces the queue service for solo volumes; past thousands of jobs/hour, graduate to real queues (the features post covers the $0 cron-to-queue path). Own simplicity until scale demands rent.

### When do timeouts actually protect me (vs just billing)?
Runaway-loop protection: infinite recursion dies at the clock instead of billing forever — genuine (if expensive) insurance. And architectural discipline: clocks force async thinking that scales. The honest split: be grateful for the guardrail, resentful of the meter, and relocate workloads where the guardrail costs architecture instead of milliseconds. Protection welcome; per-ms pricing on failure, never.

## Keep reading

- [$900 Vercel Bill? How Surprise Invoices Happen](/blog/vercel-surprise-bill-900-fix) — duration in the four-meter lineup
- [Vercel's 100GB Limit Dies on Day 13 (Do This)](/blog/vercel-bandwidth-100gb-limit-overage) — the sibling meter, same escape shape
- [We Left Vercel at $1,800/mo for a $5 VPS](/blog/vercel-1800-month-to-5-vps) — the full exit when meters (this one included) win
