Skip to content

Deploy NextPDF Connect

REST and gRPC transports run in RoadRunner worker pools. The package includes three RoadRunner profiles: HTTP only, gRPC only, and a combined profile. The MCP transport runs as a plain subprocess and does not need a supervisor.

Terminal window
composer require nextpdf/server
./vendor/bin/rr get-binary

RoadRunner is the process supervisor. It owns the worker pool, restarts workers under memory pressure, and routes each request to an available worker. The PHP package provides the worker entry point: bin/nextpdf-server for HTTP, and bin/nextpdf-grpc for gRPC. RoadRunner wraps that entry point with a pool. Each worker handles one request at a time.

The MCP transport works differently. bin/nextpdf-mcp is a single PHP process. It speaks JSON-RPC over stdio, and the client starts it directly.

ProfileTransportsCommand
.rr.yamlREST onlyrr serve -c .rr.yaml
.rr.grpc.yamlgRPC onlyrr serve -c .rr.grpc.yaml
.rr.full.yamlREST + gRPCrr serve -c .rr.full.yaml

The HTTP profile binds the REST listener to 0.0.0.0:8080. It exposes a status endpoint on :2114 and metrics on :2112. It sizes the worker pool from NEXTPDF_WORKER_COUNT, which defaults to four. In the shipped profiles, the supervisor limits each worker to 256 MB.

The combined profile adds the gRPC worker pool on tcp://0.0.0.0:9090. It sizes that pool from NEXTPDF_GRPC_WORKER_COUNT, which defaults to two. It also configures gRPC mutual TLS.

Terminal window
export NEXTPDF_API_KEYS_FILE=/run/secrets/api-keys
./vendor/bin/rr serve -c .rr.full.yaml

Run a production container with the combined profile, file-based keys, and Redis-backed shared stores:

docker/docker-compose.yml (production shape)
services:
nextpdf-connect:
image: ghcr.io/nextpdf-labs/server:latest
command: ["rr", "serve", "-c", "/app/.rr.full.yaml"]
ports:
- "8080:8080" # REST
- "9090:9090" # gRPC
environment:
- NEXTPDF_API_KEYS_FILE=/run/secrets/api-keys
- NEXTPDF_WORKER_COUNT=8
- NEXTPDF_GRPC_WORKER_COUNT=4
- NEXTPDF_REDIS_HOST=redis
secrets:
- api-keys
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
redis:
image: redis:8
  • In-memory stores do not span workers. Without Redis, each worker keeps its own rate-limit, idempotency, and document stores. In a multi-worker pool, in-memory stores produce inconsistent rate limiting and can lose documents across workers. For any pool larger than one worker, set NEXTPDF_REDIS_HOST and install ext-redis. The server automatically falls back to in-memory stores if the Redis connection fails. Verify Redis health; do not assume it.

  • The job store is SQLite in WAL mode. Async jobs persist to one SQLite file shared by all HTTP and gRPC workers. Put that file on a volume that all workers can write to. When a worker shuts down, it marks its still-running jobs as failed on a best-effort basis, so those jobs are not left orphaned.

  • bin/nextpdf-prune is a maintenance entry point. It ships in the repository, not in vendor/bin/. Invoke it directly for store pruning tasks. It is not a server transport.

  • The image PHP version may not have ext-redis. The shipped Dockerfile builds ext-redis on a best-effort basis. It continues without the extension if no compatible release exists for the base PHP. Confirm that the extension is present in the running image before you rely on Redis-backed stores.

Set NEXTPDF_WORKER_COUNT for the available CPU and memory. Each worker is a PHP process limited by the supervisor memory ceiling. For render-heavy workloads, start with one worker per core, then tune against the metrics endpoint. Size the gRPC pool independently. It is typically smaller than the HTTP pool, because gRPC clients are usually fewer and longer-lived.

The combined profile configures the gRPC transport for mutual Transport Layer Security (TLS): the server presents a certificate, then requires and verifies a client certificate. Supply the server key, server certificate, and client CA as deployment secrets, not baked into the image. Generate and rotate them out of band; do not run the gRPC transport with a plaintext listener on an untrusted network.

The REST profile binds a plaintext HTTP listener. Terminate TLS in front of it with a reverse proxy, load balancer, or service mesh, and bind the listener to the internal network only. Forward the client identity through the Authorization header unchanged; the server performs its own key validation. The listener does not provide secure transport on its own; this deployment configuration does.

Mount API keys with NEXTPDF_API_KEYS_FILE pointing to a secret file instead of using the inline NEXTPDF_API_KEYS variable. The file-based store hot-reloads on change, so rotation needs no restart. Never bake keys or TLS private material into the image. See /connect/security-and-operations/.

Deployment mechanics make no normative protocol claims. Authentication and transport-security citations are pinned on /connect/security-and-operations/.

Install nextpdf/premium into the image to register the additional Pro and Enterprise tools inside the same workers. No separate process or port is involved. You set the packaging boundary at image build time.

  • /connect/configuration/ — worker count, Redis, and tier ceilings
  • /connect/security-and-operations/ — TLS, mutual TLS, secrets, threat model
  • /transports/rest/ · /transports/grpc/ — per-transport runtime detail
  • /connect/production-usage/ — health probes, scaling, and observability
  • /connect/troubleshooting/ — diagnosing worker, store, and auth failures