« All posts

A Memory-Bounded HTML-to-PDF Architecture for Bursty Workloads

How a solo-built PDF service uses a Postgres-backed queue and streaming Chromium renders to cap memory use regardless of document size.

Generating a PDF is trivial; the hard part is the production shape around it — documents ranging from 50 KB to hundreds of megabytes, traffic that arrives in unpredictable bursts, sensitive data by default, and a system that a solo operator can actually run. DocPenny was built solo in twelve weeks around exactly those constraints, using a deliberately minimal stack: SvelteKit, Hono, pg-boss running on Postgres, Chromium, and S3-compatible storage — no Redis, no Kafka, no Kubernetes.

The core architectural decision is decoupling ingestion from rendering via the queue. Load tests show the API accepting roughly 5,900 documents per second while Chromium renders about 63 per second. That 90x gap isn't a bottleneck to fix — it's the design: the queue absorbs spikes, workers drain at their own pace, and nothing gets dropped.

Memory is bounded the same way. Instead of buffering the full PDF, the pipeline uses Chrome DevTools Protocol's streaming print mode, pulling 1 MB chunks through a backpressure-aware Node stream directly into an S3 multipart upload. The result is a fixed ~20 MB memory footprint per document regardless of output size. Security follows a similar separation of concerns: templates hold structure only, while the actual data payload is encrypted with AES-256-GCM using a per-payload HKDF-derived key, webhooks are HMAC-signed, and artifacts are purged on short TTLs.

The entire system runs on a slice of a shared server costing about €45/month — a concrete case study in how a minimal-dependency stack can make solo-operated SaaS both technically and financially sustainable.