Deploy and operate
Size, cache, queue, and monitor PDF rendering on a tested Node target.
Nuxt PDF renders on Node. The engine, the templates, and the embedded assets live in the Nitro server output. The client bundle contains none of them. For a separate Node worker, use the standalone registry build.
Pick a runtime
| Runtime | Support | Notes |
|---|---|---|
| Node server (default Nitro preset) | Supported | The tested path. Use it for high-volume or large documents. |
| Standalone Node registry | Locally tested | Compile with the build entry. Test the deployed worker before production use. |
| Convex Node action | Not deployment-verified | A candidate for the standalone registry. Measure bundle size, memory, and execution limits on the target deployment. |
| Vercel serverless build | Build-verified | The package test verifies the Vercel Nitro output. Execute your deployed function before production use. |
| Other Node serverless functions | Expected, not verified | Use a Node-compatible Nitro preset and test the deployed runtime. |
| Edge workers | Not supported | The engine needs Node APIs. Do not target edge presets. |
Size the memory budget
One render holds the document tree, the laid-out pages, and the output bytes in memory at the same time. Give each function or container headroom above your largest expected document:
- Start from
pdf.limits.maxOutputBytes(64 MB by default) plus the tree and image budgets. - Lower
pdf.limits.maxPagesandpdf.limits.maxOutputBytesto cap the worst case for untrusted input. - Load-test with your real templates before you pick an instance size.
Align timeouts
The render deadline lives in pdf.limits.timeoutMs (30 seconds by default).
The platform timeout must stay longer than that budget, or the platform kills
a render that Nuxt PDF would have finished.
export default defineNuxtConfig({
pdf: {
limits: {
timeoutMs: 10_000,
},
},
})Set the platform function timeout to at least the same value plus queueing headroom.
Cache completed renders
Cache a PDF only when your application controls every input to the render. A template can read the current time or imported application code. An allowlisted remote image can also change without a props change. Nuxt PDF does not promise that these renders produce the same bytes.
Build the cache key from the template version and the canonical render inputs. Include the tenant or authorization boundary when documents differ between customers. Store the completed bytes in a bounded cache, object store, or CDN with an explicit expiry and invalidation rule.
Do not use a module-level Map as a production cache. It has no size limit,
does not persist reliably across serverless invocations, and can cross request
boundaries when its key is incomplete.
Queue long documents
A 200-page report can occupy a worker for seconds. Keep that work off the request path when you can:
- Accept the request and return a job identifier.
- Render in a background worker.
- Store the bytes and let the client poll or subscribe.
This pattern also keeps interactive routes responsive while batch jobs run.
Serverless notes
Local fonts and images travel inside the server bundle as validated bytes. The repository verifies the Vercel output structure but does not execute that deployed function. Test the real target before you send production traffic.
- Bundle size. Every font and image under
pdfs/adds to the bundle. Remove assets that no template uses; the module embeds only discovered files. - Cold starts. The first invoke parses the bundle. Fewer embedded assets mean faster cold starts.
- Concurrency. Size concurrency from load tests on your deployed runtime. Nuxt PDF does not currently claim cross-render concurrency guarantees.
- Filesystem access. Templates read no files at runtime. Do not add code that reads from the deployment filesystem during a render.
Monitor renders
Every render returns content-free diagnostics: duration, byte length, page count, layout passes, layout warnings, and registered font faces. Log them next to your request logs to spot slow or oversized documents before users do.
export default defineEventHandler(async () => {
const result = await pdf.invoice.render(props)
console.info('invoice rendered', result.diagnostics)
return result.response()
})