CloudPDF
DocsPricing
Start building

How it works

The server is a single Node process with three responsibilities: speak HTTP, render PDFs, and persist documents. Understanding the pieces makes every configuration choice obvious.

The big picture#

        ┌──────────────┐        Bearer JWT        ┌───────────────────────────┐
        │ Your frontend│ ───────────────────────▶ │     @cloudpdf/server      │
        │ @cloudpdf/   │                           │                           │
        │   engine     │ ◀─────────────────────── │  Fastify HTTP API         │
        └──────────────┘   pages, text, metadata   │      │                    │
                                                    │      ▼                    │
        ┌──────────────┐   mints doc-scoped JWT     │  Worker pool (PDFium)     │
        │ Your backend │ ─────────────────────────▶│      │                    │
        └──────────────┘                            │      ▼                    │
                                                    │  Database   Object store  │
                                                    └───────┼──────────┼────────┘
                                                            ▼          ▼
                                                   SQLite/Postgres  disk / S3 /
                                                                    GCS / Azure
  1. Your backend mints a token. When a user needs a document, your backend issues a short-lived, document-scoped JWT signed with the shared CLOUDPDF_JWT_SECRET.
  2. The frontend calls the server with that token via @cloudpdf/engine. The token says which document and what the caller may do.
  3. The server verifies the token, loads the document from the object store (caching it locally), and hands the work to a native PDFium worker.
  4. Results come back as JSON or images — rendered pages, extracted text, annotations, metadata.

The HTTP API#

A Fastify server exposes two route families:

RoutesAuthPurpose
/v1/docs/*Document-scoped JWTRender pages, extract text, read/write annotations and metadata, unlock encrypted PDFs. This is what @cloudpdf/engine calls.
/v1/admin/*Admin JWTCreate documents, upload files, mint and revoke tokens. This is what your backend calls.
/healthz, /readyzNoneLiveness and readiness probes.

Every document and admin route requires a valid token; an unauthenticated call returns 401. See Authentication for how tokens are scoped.

The native worker pool#

PDFium is a native C++ library. The server runs it inside a pool of Node worker_threads so that CPU-heavy rendering never blocks the HTTP event loop and several requests can render in parallel.

The pool size is controlled by CLOUDPDF_WORKER_POOL_SIZE. The default is conservative — min(2, cpu count) — and you raise it as you give the process more cores and memory.

Native code parsing hostile PDFs will eventually crash. Two layers keep that survivable: set CLOUDPDF_ENGINE_ISOLATION=host so a crash costs a sub-second engine respawn inside the instance (documents that repeatedly crash the engine are quarantined automatically), and run multiple replicas so even an instance restart never means zero capacity. See Health & scaling.

Storage: database + object store#

The server separates metadata from bytes:

  • Database — document records, annotations, tokens, and the audit log. Use SQLite (a single file, zero setup) to start, or Postgres for multi-replica production. See Database.
  • Object store — the actual PDF bytes and rendered artifacts. Use the local filesystem for a single host, or S3 / GCS / Azure Blob for durable, shared storage. See Storage.
  • Cache — a local directory of recently used documents and renders so repeat requests are fast. It is disposable; the server rebuilds it from the object store.

This split is what lets the same binary run as a zero-config single file and as a horizontally-scaled cluster — you only change configuration.

The smallest possible setup — SQLite + local disk + local cache — needs no external services at all. That is exactly what the Quick start uses.

Twelve-factor by design#

Everything is configured through CLOUDPDF_* environment variables, the process logs to stdout, and state lives in the database, object store, and cache — never in the container. That is what makes the same image safe to run as one container or fifty pods.

Next steps#