NextPDF draaien op serverlessplatforms
In een oogopslag
Sectie met titel “In een oogopslag”De native, in-process NextPDF core-engine is een bijna ideale serverless-workload.
Het is pure PHP die binnen je proces draait — composer require nextpdf/core,
bouw een document, ontvang de bytes. Er is geen externe binary om te spawnen, geen
headless browser, geen daemon om in leven te houden, en geen socket naar een
sidecar-service. Een functie die een PDF bouwt start koud, draait je PHP, retourneert
de bytes en eindigt. Dat past schoon op AWS Lambda (via de Bref-runtime), Google
Cloud Run en AWS App Runner.
Deze pagina behandelt het implementeren van die native engine op die drie runtimes en de kleine set echte beperkingen die ze opleggen:
- het runtime-filesystem is niet duurzaam: Lambda garandeert alleen een
schrijfbare
/tmp, terwijl container-runtimes (Cloud Run, App Runner) een vluchtig, container-gebonden filesystem hebben — hoe dan ook moeten lettertypen binnen het deployment-package of de image meereizen en geregistreerd zijn in PHP (de engine leest geen omgevingsvariabele voor het lettertypepad); - cold starts betalen voor autoloading en eventuele lettertype-warmup, dus warm
de
FontRegistryéén keer per container, niet per aanroep; - packagegrootte, geheugen en timeout moeten worden afgestemd op de build, niet op een triviale request.
Deze pagina is alleen voor de native engine. De Chrome-brug
(writeHtmlChrome via het voorgestelde nextpdf/artisan-package) is een ander,
zwaarder verhaal: hij shelt uit naar een headless Chromium via symfony/process,
wat een gewone Lambda-zip of een slanke container niet bevat. Chromium op Lambda
draaien betekent een custom layer met de browser en zijn gedeelde libraries, veel
grotere packages, en veel langere cold starts — buiten scope hier. De kale engine
heeft niets daarvan nodig.
Bevestig voordat je begint dat deze onderdelen op hun plek staan:
- Je applicatie heeft een gecommitte
composer.jsonencomposer.lock, metnextpdf/coreals afhankelijkheid. - Je hebt de lettertypebestanden die je wilt insluiten, en je hebt het recht ze in te sluiten.
- Je hebt de toolchain voor je doel — de Bref-CLI en het
serverless-framework voor Lambda, of een container-build voor Cloud Run / App Runner.
Waarom de native engine bij serverless past
Sectie met titel “Waarom de native engine bij serverless past”Rechtstreeks uit het package gelezen vereist nextpdf/core php: >=8.4 <9.0 en een
kleine set PHP-extensies — ext-mbstring, ext-intl, ext-gd, ext-openssl,
ext-zlib en ext-curl. De standaard Bref-PHP-layers leveren ieder daarvan mee. De
officiële php:8.4-container-images leveren openssl, curl en zlib standaard,
maar mbstring, gd en intl zijn niet meegeleverd — ze vereisen het
installeren van systeemafhankelijkheden en het inschakelen van de extensies met
docker-php-ext-install (zie de
Docker-implementatiehandleiding).
Op Bref valt er niets exotisch te compileren; op het containerpad schakel je die
drie extensies in tijdens de image-build voor de kale engine.
Wat de geschiktheid schoon maakt is wat de engine niet doet:
- Geen subproces voor het core-pad. Een document bouwen en
getPdfData()aanroepen is van begin tot eind in-process PHP. De afhankelijkheidsymfony/processbestaat voor de optionele Chrome-brug, niet voor native rendering — native PDF-generatie spawnt nooit een proces. - Geen persistente staat. Elke aanroep bouwt een vers document en retourneert bytes. Niets hoeft tussen requests te overleven behalve de warme container, die je benut voor lettertype-warmup (hieronder) maar nooit voor correctheid vertrouwt.
- Geen schrijfbare werkmap nodig. De engine bouwt de PDF in het geheugen en
retourneert hem als een string; hij raakt de schijf alleen aan als jij
save()aanroept. Op serverless doe je dat niet — je retourneert de bytes — dus het ontbreken van een duurzaam filesystem bijt het build-pad nooit.
De ene harde beperking: geen duurzaam schrijfbaar filesystem
Sectie met titel “De ene harde beperking: geen duurzaam schrijfbaar filesystem”Het deployment-filesystem is niet duurzaam, maar het model verschilt per runtime.
AWS Lambda garandeert alleen een schrijfbare /tmp (512 MB standaard,
configureerbaar tot 10 GB); de rest van het functie-filesystem is alleen-lezen.
Container-runtimes (Cloud Run, App Runner) hebben een vluchtig, container-gebonden
schrijfbaar filesystem in plaats van een /tmp-only-model — maar alles wat daar
wordt geschreven gaat verloren wanneer de container wordt gerecycled, dus het is
kladruimte, geen opslag. Geef in elk geval de voorkeur aan /tmp of een
geconfigureerd volume voor staging, en vertrouw nooit op schrijfacties naar het
applicatie-image-pad als duurzame opslag. Twee gevolgen volgen hieruit.
Roep nooit save() aan in de verwachting van duurzame uitvoer.
NextPDF\Core\Document biedt zowel save(string $path): void als
getPdfData(): string. Op serverless gebruik je getPdfData() en retourneer of
upload je de bytes — behandel een schrijfactie naar de applicatiemap niet als
persistente opslag. Als je een bestand moet stagen (bijvoorbeeld om met multipart te
uploaden naar objectopslag), schrijf dan onder /tmp (of een geconfigureerd volume)
en ruim op, met in gedachten dat deze kladruimte op een warme container blijft bestaan
over aanroepen heen en meetelt bij de groottelimiet ervan.
use NextPDF\Core\Document;
// Right for serverless: get the bytes, return or upload them.$pdf = $document->getPdfData(); // string of PDF bytes, built in memory
// Avoid on serverless: save() writes to disk. On Lambda the application// directory is read-only; on Cloud Run / App Runner it is writable but// ephemeral (lost on container recycle). Neither is durable storage.// $document->save('/var/task/out.pdf'); // not durable — return the bytes insteadInstalleer geen OS-lettertypen tijdens runtime, en vertrouw niet op automatische
lettertypeontdekking; lever je lettertypebestanden mee voor productie. Op Lambda
blokkeert het alleen-lezen-filesystem apt-get install fonts-* ronduit; op een
container-runtime belandt elke runtime-installatie op een vluchtig filesystem en gaat
verloren bij de volgende recycle. En het zou sowieso niet helpen, omdat de native
engine geen OS/fontconfig-lettertypen leest — hij herleidt lettertypen alleen uit
bestanden die je registreert. Dus voor productie moeten de lettertypebestanden binnen
het deployment-artefact meereizen. Als je bewust lettertypebestanden naar /tmp of
een geconfigureerd volume haalt, moet je ze expliciet registreren bij de
lettertyperegistry en de extra cold-start- en betrouwbaarheidskosten accepteren — het
is geen aanbevolen productiepatroon.
Lettertypen meeleveren en registreren in het package of de image
Sectie met titel “Lettertypen meeleveren en registreren in het package of de image”De native engine herleidt lettertypen uit lettertypebestanden via de
NextPDF\Typography\FontRegistry, niet uit fontconfig of OS-geïnstalleerde
lettertypen. Op serverless is dit niet onderhandelbaar: er is geen persistent
filesystem om lettertypen na deploy op te zetten, dus ze reizen binnen het package
mee (een Lambda-zip of -layer) of binnen de image (Cloud Run / App Runner).
Lever je .ttf / .otf / .ttc-bestanden mee onder een map in je project —
resources/fonts/ is de conventie — zodat ze in het artefact worden opgenomen.
Registreer die map vervolgens in PHP. De engine leest geen omgevingsvariabele
voor het lettertypepad: NEXTPDF_FONTS_PATH is de standaardwaarde van de
configuratiesleutel fonts_path van het nextpdf/laravel-package
(env('NEXTPDF_FONTS_PATH', resource_path('fonts'))) en wordt alleen geconsumeerd
door die framework-integratie, niet door nextpdf/core. Een kale functie moet de
registry construeren met de meegeleverde map:
use NextPDF\Typography\FontRegistry;use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;
// Register the directory the deployment artifact bundled the fonts into.// On Lambda/Bref the code root is /var/task; adjust for your runtime.$registry = new FontRegistry(__DIR__ . '/resources/fonts');// (equivalently, $registry->addFontDirectory(__DIR__ . '/resources/fonts');)
$factory = new DocumentFactory($registry, new ImageRegistry(maxCacheBytes: 0));$document = $factory->create();Dat is de hele serverless-zorg voor lettertypen. De regels voor bestandsnamen, de volledige registry-API en de afhandeling van het niet-duurzame filesystem staan op de gewijde pagina — dupliceer ze hier niet. Lees Lettertypen voor de native engine in productie aanleveren voor het volledige patroon, en registreer dezelfde map die je hebt meegeleverd. De Docker-implementatiehandleiding behandelt het equivalente meeleveren aan de image-kant voor het geval Cloud Run / App Runner.
Cold starts: warm de FontRegistry één keer per container
Sectie met titel “Cold starts: warm de FontRegistry één keer per container”Een cold start betaalt voor de PHP-bootstrap, Composers geoptimaliseerde autoloader, en eventuele lettertypeparsing die de eerste build oproept. De bootstrap kun je niet vermijden, maar je kunt lettertypewerk uit het hete pad halen en het hergebruiken over warme aanroepen heen.
Construeer de FontRegistry en DocumentFactory één keer, buiten de handler,
zodat ze leven voor de levensduur van de container en op elke warme aanroep worden
hergebruikt. Roep optioneel warmup() aan met de lettertypebestanden waarvan je weet
dat je ze zult gebruiken, zodat ze tijdens de initialisatie worden geparseerd in
plaats van bij de eerste render, en lock() daarna de registry zodat zijn geparseerde
staat is bevroren en geen mutatie per aanroep een race kan veroorzaken:
use NextPDF\Typography\FontRegistry;use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;
// Container-scoped, built once at cold start (module scope, not per request).$fontsDir = __DIR__ . '/resources/fonts';$registry = new FontRegistry($fontsDir);
// Parse the fonts you will actually use now, so the first render does not.$registry->warmup([ $fontsDir . '/liberation/LiberationSans-Regular.ttf', $fontsDir . '/liberation/LiberationSans-Bold.ttf',]);
// Freeze the parsed state for the life of the warm container.$registry->lock();
$factory = new DocumentFactory($registry, new ImageRegistry(maxCacheBytes: 0));
// Each invocation: fresh document from the shared, warm factory.$handler = static function (array $event) use ($factory): string { $document = $factory->create(); $document->addPage(); $document->cell(0, 10, 'Hello from serverless', newLine: true);
return $document->getPdfData();};Roep warmup() aan voordat je lock() aanroept — de registry wordt bevroren
zodra hij is gelockt, dus een warmup daarna wekt een configuratiefout op. Behandel een
lettertype dat bij warmup niet laadt als een deploy-time-fout, geen
runtime-detail: valideer dat elk lettertypepad dat je wilt warmen bij de start
daadwerkelijk bestaat en parseert, en laat de deploy (of je health check) falen als
dat niet zo is, in plaats van een verkeerd getypt pad later te laten opduiken als
ontbrekende glyphs. Houd de warmup-lijst beperkt tot de lettertypen die een typische
aanroep nodig heeft; een grote familie warmen die je zelden gebruikt verlengt alleen
elke cold start.
Een Bref-functie op AWS Lambda
Sectie met titel “Een Bref-functie op AWS Lambda”Bref levert de PHP-runtime voor Lambda als een gepubliceerde
layer en een serverless.yml-plugin. De php-84-runtime levert al de extensies mee
die nextpdf/core nodig heeft, dus je deployt je code en lettertypen en wijst een
functie naar een handler. Een minimale serverless.yml:
service: nextpdf-serverless
provider: name: aws region: us-east-1 runtime: provided.al2023
plugins: - ./vendor/bref/bref
functions: generate: handler: handler.php description: Generate a PDF with the native NextPDF engine runtime: php-84 memorySize: 1024 # size to the build; see "Sizing" below timeout: 30 # seconds; raise for large documents # The Lambda filesystem is read-only except /tmp. Fonts ship in the # package under resources/fonts and are registered in the handler.De handler bouwt het document met de warme, container-gebonden factory en retourneert
de bytes. Voor een HTTP-API retourneer je ze base64-gecodeerd met het content type
application/pdf zodat API Gateway de body als binair behandelt; voor een invoke- of
queue-trigger upload je de bytes naar objectopslag en retourneer je de key:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
// --- Cold-start: built once per container, reused across warm invocations. ---$fontsDir = __DIR__ . '/resources/fonts';$registry = new FontRegistry($fontsDir);$registry->warmup([$fontsDir . '/liberation/LiberationSans-Regular.ttf']);$registry->lock();$factory = new DocumentFactory($registry, new ImageRegistry(maxCacheBytes: 0));
// --- Per-invocation handler. ---return static function (array $event) use ($factory): array { $document = $factory->create(); $document->addPage(); $document->cell(0, 10, 'Invoice', newLine: true);
// getPdfData() materializes the whole PDF in memory and returns it. $bytes = $document->getPdfData();
return [ 'statusCode' => 200, 'isBase64Encoded' => true, 'headers' => ['Content-Type' => 'application/pdf'], 'body' => base64_encode($bytes), ];};Verifieer dat het package een gezonde omgeving bevat voordat je verkeer ernaartoe
bedraadt. nextpdf/core levert een CLI mee, geïnstalleerd op vendor/bin/nextpdf,
waarvan het commando doctor precies rapporteert over de extensies die de engine
nodig heeft. Draai het één keer tegen dezelfde runtime-image of -layer om te bevestigen
dat PHP 8.4 en elke vereiste extensie aanwezig is.
Cloud Run en App Runner
Sectie met titel “Cloud Run en App Runner”Cloud Run en App Runner draaien een container in plaats van een gezipte functie,
dus de build is de Docker-image uit
Een NextPDF-applicatie containeriseren,
geen Bref-package. De native-engine-beperkingen zijn identiek: lever de lettertypen
mee in de image, registreer de meegeleverde map in PHP, draai zonder privileges, en
behandel het filesystem als niet-duurzaam. Anders dan het /tmp-only-model van Lambda
heeft een Cloud Run / App Runner-container een vluchtig, container-gebonden schrijfbaar
filesystem — maar het wordt gereset bij elke recycle, dus gebruik /tmp (een tmpfs op
Cloud Run) of een geconfigureerd volume voor klad en vertrouw nooit op schrijfacties
naar het applicatie-image-pad als duurzame opslag.
De verschillen met Lambda zijn operationeel, niet structureel:
- De container kan warm blijven over requests heen onder een
concurrency-instelling, dus de container-gebonden
FontRegistry/DocumentFactory- warmup hierboven loont over veel requests, niet alleen de volgende aanroep. - Je serveert over HTTP (een FPM- of ingebouwde PHP-server-SAPI) in plaats van een invoke-event, dus je retourneert de bytes via de response van je framework. Voor een groot document retourneer je ze als een gestreamde response — zie Een groot gegenereerd PDF als HTTP-response streamen.
- De request-timeout en het geheugen worden ingesteld op de service (Cloud Run service-timeout / geheugen; App Runner instance-configuratie) in plaats van per functie.
Al het overige — de extensieset, de lettertyperegistratie, de getPdfData()-
uitvoeraanroep — is dezelfde code als de Lambda-handler.
Afstemmen: package, geheugen en timeout
Sectie met titel “Afstemmen: package, geheugen en timeout”- Package- en image-grootte. Het artefact draagt
vendor/(alleen productie — installeer met--no-dev) en je meegeleverde lettertypen. Lettertypen domineren: een volledige CJK-familie is tientallen megabytes. Lever alleen de lettertypen mee die je daadwerkelijk rendert om het Lambda-package onder zijn limieten en de image klein te houden, wat ook de cold starts verkort. De meegeleverde Liberation-familie (resources/fonts/liberation/) is klein en dekt metrisch-compatibele Helvetica-substitutie. - Geheugen.
getPdfData()bouwt het hele document in het geheugen en retourneert het als één string, dus het piekgeheugen is ongeveer de grootte van één voltooide PDF plus de werkset van de build. Stem het functie-/container-geheugen af op het grootste document dat je genereert, niet op een gemiddelde. Op Lambda schaalt geheugen ook de CPU, dus meer geheugen betekent vaak een snellere build en een goedkopere run ondanks het hogere tarief per milliseconde — meet beide. Een document van een paar pagina’s zit comfortabel op 512–1024 MB; beeldzware of veelpagina’s- documenten hebben meer nodig. - Timeout. De build, niet de overdracht, domineert het requestbudget. Stel de functie-timeout boven de slechtste-geval-buildtijd in met marge. Als een document groot genoeg is om een timeout te riskeren, verplaats generatie dan naar een asynchrone trigger (een queue-backed Lambda of een Cloud Run-job) die het resultaat naar objectopslag schrijft in plaats van een synchrone request te blokkeren.
/tmp-grootte. Als je iets onder/tmpstaget, houd dan rekening met de groottelimiet ervan en onthoud dat het over warme aanroepen heen blijft bestaan — ruim op, of een langlevende container vult het langzaam.
Randgevallen en valkuilen
Sectie met titel “Randgevallen en valkuilen”- Geen duurzame
save()naar de app-map. Het deployment-filesystem is niet duurzaam — de app-map van Lambda is alleen-lezen (alleen/tmpaccepteert schrijfacties), en een Cloud Run / App Runner-container-filesystem is schrijfbaar maar vluchtig. GebruikgetPdfData()en retourneer/upload de bytes; stage onder/tmpof een geconfigureerd volume als het moet. - Vertrouw niet op automatische lettertypeontdekking. Installeer geen
OS-lettertypen tijdens runtime, en vertrouw niet op automatische lettertypeontdekking;
lever je lettertypebestanden mee voor productie. De native engine leest geen
OS/fontconfig-lettertypen — hij herleidt alleen bestanden die je registreert. Als je
bewust lettertypebestanden naar
/tmpof een geconfigureerd volume haalt, moet je ze expliciet registreren bij de lettertyperegistry en de extra cold-start- en betrouwbaarheidskosten accepteren. Lever de bestanden mee en registreer ze. Zie de hierboven gelinkte lettertypepagina. NEXTPDF_FONTS_PATHdoet niets voor de kale engine. Het is denextpdf/laravel-configuratiestandaard, geen variabele dienextpdf/coreleest. Een kale Bref-handler die alleen die variabele instelt registreert geen lettertypen en rendert tofu.- De Chrome-brug past niet in een gewone functie.
writeHtmlChromeheeft een headless Chromium en hetsymfony/process-subprocespad nodig. Chromium op Lambda zetten vereist een custom layer met de browser en zijn libraries, veel grotere packages, en lange cold starts. De native engine enwriteHtmlhebben niets daarvan nodig — geef er de voorkeur aan op serverless. - Cold-start-kosten zijn autoload plus lettertypeparsing. Gebruik
--optimize-autoloaderop de productie-installatie en warm de registry één keer per container. Warm geen lettertypen die je zelden gebruikt. - API Gateway heeft binaire afhandeling nodig. Retourneer
isBase64Encoded: truemetContent-Type: application/pdf, en configureer de API omapplication/pdfals een binair mediatype te behandelen, anders ontvangt de client beschadigde bytes. - Premium en ionCube zijn een zwaardere artefactzorg. ionCube-gecodeerde NextPDF Pro / Enterprise-builds hebben de ionCube Loader nodig die overeenkomt met de exacte PHP-build in de runtime, wat een standaard Bref-layer niet bevat. Dat is buiten scope voor een core-serverless-deploy.
Beveiligingsnotities
Sectie met titel “Beveiligingsnotities”- Lever geen dev-afhankelijkheden mee. Installeer met
--no-devzodat de test- en analysetooling nooit het functie-package of de image binnenkomt. - Valideer invoer vóór het bouwen. Een PDF-build aangedreven door request-invoer is een vector voor geheugenuitputting; wijs out-of-range- of te grote invoer af aan de grens voordat enig buildwerk draait, en begrens concurrency zodat hoog verkeer het piekgeheugen niet vermenigvuldigt tot een out-of-memory-fout.
- Houd lettertypen en licenties uit openbare artefacten. Lever alleen lettertypen mee die je het recht hebt in te sluiten, en bak nooit een premium-licentiebestand in een publiek gepushte image of layer — lever het in plaats daarvan tijdens runtime aan via een omgevingswaarde of secret manager.
- Least privilege. Geef de functie/service alleen de IAM-permissies die hij nodig heeft (bijvoorbeeld schrijftoegang tot de ene output-bucket), en draai de container zonder privileges zoals de Docker-handleiding laat zien.
Conformiteit
Sectie met titel “Conformiteit”Deze handleiding doet geen normatieve standaardenclaim. De platformfeiten worden
rechtstreeks uit het nextpdf/core-package gelezen: de beperking php: >=8.4 <9.0 en
de vereiste extensies ext-mbstring, ext-intl, ext-gd, ext-openssl, ext-zlib
en ext-curl. De standaard Bref-PHP-8.4-runtime-layer levert alle zes mee; de
officiële php:8.4-image levert openssl, curl en zlib, maar mbstring, gd en
intl moeten worden geïnstalleerd en ingeschakeld tijdens de image-build met
docker-php-ext-install (zie de Docker-pagina). De uitvoeraanroep is het echte
core-oppervlak
NextPDF\Core\Document::getPdfData(): string (de schijf-tegenhanger ervan is
save(string $path): void). Lettertypen worden geregistreerd via
NextPDF\Typography\FontRegistry — het map-constructorargument ervan /
addFontDirectory(), met warmup(array $fontFiles) en lock() voor het
cold-start-patroon — bedraad via NextPDF\Core\DocumentFactory::create().
NEXTPDF_FONTS_PATH is de configuratiesleutel fonts_path van het
nextpdf/laravel-package
(env('NEXTPDF_FONTS_PATH', resource_path('fonts'))), geen variabele die
nextpdf/core leest. Het commando doctor van de nextpdf-CLI is gedeclareerd als
"bin": ["bin/nextpdf"] in het package en geïnstalleerd op vendor/bin/nextpdf in
een consumerende app. De Bref-runtime-namen en het gedrag van AWS Lambda / Cloud Run /
App Runner zijn de gedocumenteerde features van die leveranciers.
Zie ook
Sectie met titel “Zie ook”- Een NextPDF-applicatie containeriseren: de productie-image die wordt gebruikt voor de Cloud Run / App Runner-doelen.
- Lettertypen voor de native engine in productie aanleveren: de lettertype-bestandsnaamgeving, de registry-API en het warmup-and-lock-patroon waar deze pagina op steunt.
- Een groot gegenereerd PDF als HTTP-response streamen: het geheugenmodel voor het retourneren van een gebouwd document over HTTP op Cloud Run / App Runner.
- Renderen aan de edge met Cloudflare: wanneer een in-process functie niet de juiste runtime is.