Generating PDFs in Laravel is deceptively expensive. What works perfectly in development
can easily collapse under real traffic due to blocking requests, memory spikes, and CPU saturation.
This guide focuses on building a high-performance, production-safe PDF generation pipeline
using queues, strict memory limits, and streaming strategies.
Why High-Performance PDF Generation Matters
PDF generation is not a simple rendering task. It involves HTML parsing, layout calculation,
font embedding, and binary output. When handled synchronously, it can:
Block HTTP requests
Cause PHP memory exhaustion
Slow down unrelated requests
Crash workers under concurrency
⚠️ Never generate large PDFs synchronously in a controller on a production system.
Queue-Based PDF Generation Architecture
The foundation of high-performance PDF generation is asynchronous processing.
Laravel queues allow us to move CPU- and memory-heavy work outside the request lifecycle.
Installing Redis Queue Dependencies
composer require predis/predis
Configure Redis and queue settings in your environment file:
REDIS_CLIENT=predis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
QUEUE_CONNECTION=redis
Running Queue Workers with Memory Limits
php artisan queue:work redis --memory=256 --timeout=120
This ensures workers are restarted before memory leaks accumulate.
Dispatching PDF Jobs Instead of Blocking Requests
The controller’s responsibility should be minimal: dispatch the job and return immediately.
Route::get('/shipments/pdf', function () {
$order = 'IB-2025-000001';
GenerateShipmentPdf::dispatch(
orderNo: $order,
url: 'https://yourdomain.com/order/IB-2025-000001'
);
return response()->json([
'status' => 'queued',
'message' => 'PDF generation started'
]);
});
This approach guarantees low response times regardless of PDF complexity.
High-Performance PDF Job Implementation
The job class is where performance discipline matters most.
<?php
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
use Barryvdh\DomPDF\Facade\Pdf;
class GenerateShipmentPdf implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $timeout = 120;
public int $tries = 3;
public function __construct(
protected string $orderNo,
protected string $url
) {}
public function handle(): void
{
$pdf = Pdf::loadView('barcode.pdf', [
'order_no' => $this->orderNo,
'url' => $this->url,
]);
$pdf->setPaper('a4');
$pdf->setOptions([
'isHtml5ParserEnabled' => true,
'isRemoteEnabled' => false,
'isPhpEnabled' => false,
'debugCss' => false,
'debugLayout' => false,
'dpi' => 96,
]);
Storage::put(
"pdfs/shipments/{$this->orderNo}.pdf",
$pdf->output()
);
}
}
Memory Limits and Worker Stability
Unbounded memory usage is the most common reason PDF systems fail under load.
Always limit memory at the worker level.
⚠️ Never set memory_limit to unlimited. Restarting workers is cheaper than crashing servers.
Reducing DomPDF Memory Footprint
DomPDF ships with features that are unnecessary in most backend PDF workflows.
Disable everything you do not need.
$pdf->setOptions([
'isRemoteEnabled' => false,
'isPhpEnabled' => false,
'debugCss' => false,
'debugLayout' => false,
]);
Blade Rules for PDF Templates
No background images
No web fonts
Inline styles only
Avoid large SVG files
PDF templates should be treated as rendering documents, not web pages.
Chunking Large Data Sets
Large reports and invoices must never load all records into memory.
OrderItem::where('order_id', $id)
->chunk(200, function ($items) {
// Render partial rows
});
Streaming vs Buffering PDF Output
How you deliver the PDF matters as much as how you generate it.
Buffering Output (Default, Not Ideal)
Buffering holds the entire PDF in memory before sending it to the client.
This does not scale for large documents or high traffic.
Streaming Output (Preferred)
return response()->streamDownload(function () use ($pdf) {
echo $pdf->output();
}, 'shipment.pdf');
Streaming reduces peak memory usage and improves response stability under load.
Load Testing PDF Generation
Never assume performance. Measure it.
During load testing, monitor:
HTTP response time
Queue wait time
Worker memory usage
Failed and retried jobs
💡 High-performance systems are proven under load, not assumed during development.
Conclusion
High-performance PDF generation in Laravel is not about faster libraries.
It is about architecture, isolation, and discipline.
By using queues, enforcing memory limits, streaming output, and validating under load,
you can safely scale PDF generation without sacrificing system stability.
🚀 Related: Scaling Laravel queue workers horizontally with Supervisor and Redis.