Skip to content

Configuration reference

The settings of a self-hosted installation, with their defaults. You set them in .env, in the installation directory next to compose.yaml (see install).

  1. Edit .env on the server.
  2. Run docker compose up -d in the same directory. Compose recreates only the services whose settings changed.

Rules that apply to every setting:

  • Empty means unset. RENDER_RETENTION_DAYS= is the same as leaving the line out, so the default applies.
  • A bad value stops the service. The API and worker check their settings at startup. A missing or invalid value stops them with a log line that names the settings, never their values, such as Invalid environment variables: OBJECT_STORAGE_BUCKET.
  • A few settings do not go in .env. The advanced settings go in compose.override.yaml instead.

The installation does not start until these are set.

Setting Example Description
PUBLIC_URL https://pdf.example.com The address people and API clients use. Sign-in links and file URLs are built from it. Must be https:// unless it is a localhost address.
POSTGRES_PASSWORD output of openssl rand -hex 32 The database password. Use hex: other characters can break the database connection.
BETTER_AUTH_SECRET output of openssl rand -hex 32 Signs sign-in sessions and file links. At least 32 characters.
DATA_ENCRYPTION_KEY output of openssl rand -base64 32 Encrypts secrets stored in the database: webhook signing secrets, signing certificate keys, storage destination access keys, and the passwords of protected PDFs while they render. Exactly 32 random bytes in base64. Cannot be recreated if lost.

Also set LICENSE_KEY to your license key. The installation starts without it, but renders nothing; see licensing.

Setting Default Description
CERTIFICATE_SOURCE automatic Where the HTTPS certificate comes from. automatic: Let’s Encrypt, or Caddy’s own CA for an IP address or a .internal, .home.arpa or .local name. internal: Caddy’s own CA for any name. files: your own certificate.
CERTIFICATE_PATH ./certificates The directory, relative to compose.yaml, holding certificate.pem and key.pem for CERTIFICATE_SOURCE=files.
HTTP_PORT 80 The host port the proxy publishes for HTTP.
HTTPS_PORT 443 The host port the proxy publishes for HTTPS (TCP and UDP).
TRUSTED_PROXY_RANGES empty Address ranges of a CDN or load balancer in front of the server, separated by spaces, or private_ranges. Empty: the client is whoever connects.

Let’s Encrypt must reach the server on ports 80 and 443, so change HTTP_PORT and HTTPS_PORT only if something in front forwards those ports. How to use these settings: on a private network and behind a CDN or load balancer.

POSTGRES_PASSWORD, BETTER_AUTH_SECRET and DATA_ENCRYPTION_KEY are under required settings.

Setting Default Description
DATA_ENCRYPTION_KEY_PREVIOUS empty The old key while you rotate DATA_ENCRYPTION_KEY. It is only used to decrypt.
  1. In .env, move the current value of DATA_ENCRYPTION_KEY to DATA_ENCRYPTION_KEY_PREVIOUS.
  2. Set DATA_ENCRYPTION_KEY to a new value from openssl rand -base64 32, and store it with your other secrets.
  3. Run docker compose up -d. The API encrypts the stored secrets again with the new key as it starts, and retries every hour.
  4. Once docker compose logs api shows neither Webhook secret encryption failed nor Signing key encryption failed, remove DATA_ENCRYPTION_KEY_PREVIOUS and run docker compose up -d again.

Database backups taken before the rotation need the old key to restore; see the data encryption key.

Password accounts need no settings. These turn on the other sign-in methods; how to set each one up is in users and sign-in.

Setting Default Description
RESEND_API_KEY empty A Resend API key. With AUTH_EMAIL_FROM, turns on emailed sign-in codes and invitations.
AUTH_EMAIL_FROM empty The sender address for those emails, on a domain verified in Resend.
ENTRA_TENANT_ID empty The directory (tenant) ID of your Microsoft Entra ID tenant. For Sign in with Microsoft.
ENTRA_CLIENT_ID empty The application (client) ID of the app registered in that tenant.
ENTRA_CLIENT_SECRET empty A client secret of that app.

Email codes turn on only when both email settings are set. Set all three ENTRA_* settings or none: the API refuses to start with only some of them.

Setting Default Description
ANTHROPIC_API_KEY empty A Claude API key. Turns on Start with → PDF file, drafted with AI in Studio, which sends each PDF to Anthropic’s Claude.

Anthropic bills your Claude API account for each PDF: about USD 0.20 on average, and at most USD 1.50, where a conversion stops. Workspaces have no monthly limit unless a platform administrator sets one under Manage → Render limits.

These let a deployment tool set up an installation and deploy templates without anyone opening Studio. Set up from configuration explains how they behave on each start.

Setting Default Description
ADMIN_EMAIL empty This account becomes the first platform administrator, instead of someone using the setup token. Ignored once an administrator exists.
DEPLOY_WORKSPACE empty On the first start with ADMIN_EMAIL: the name of the workspace to create. Otherwise: the name of the existing workspace to issue the first deploy key in. Ignored once a deploy key exists.
DEPLOY_API_KEY empty The secret of the deploy key, an API key that can import and publish templates. You choose it: pdf_ and at least 40 letters, digits, - or _.
Setting Default Description
RENDER_RETENTION_DAYS 30 Days after creation that finished renders, their PDFs and webhook delivery records are deleted. At least 1. Lowering it deletes older renders within the hour.
RENDER_TIMEOUT_MS 60000 Milliseconds one render may run before it fails with LIMIT_EXCEEDED. From 1000 to 3600000.
WORKER_CLOSE_GRACE_MS 75000 Milliseconds a stopping worker waits for its current render. Keep it at least 15000 above RENDER_TIMEOUT_MS.
WORKER_STOP_GRACE_PERIOD 90s How long Compose waits before it kills a stopping worker. Keep it at least 15 seconds above WORKER_CLOSE_GRACE_MS.
SCRIPT_ENV empty Values that beforeRender scripts read as process.env, as comma-separated NAME=value pairs, such as ADD_WATERMARK=true.

When you raise RENDER_TIMEOUT_MS, raise both grace periods with it; see what happens to work in flight.

For example, to add a watermark outside production:

Terminal window
SCRIPT_ENV=ADD_WATERMARK=true,ENVIRONMENT_NAME=staging

Every workspace’s scripts can read these values, so never put secrets in them. A malformed list stops the API at startup. A new value applies once docker compose up -d has recreated the API; templates need no new publish. How scripts read the values is in environment values.

Setting Default Description
WEBHOOK_ALLOW_PRIVATE_NETWORKS false true lets webhooks reach addresses outside the allowed destinations, such as private addresses and this server.

Turn it on only when your webhook receivers run on your own network. While it is off, no workspace member can use a webhook to reach services inside your network.

Setting Default Description
DESTINATION_ALLOW_PRIVATE_NETWORKS false true lets storage destinations use private addresses, this server and plain http:// endpoints, such as a MinIO server next to the installation.

The API tests a destination when it is saved and the worker uploads to it, so both need the setting; set it in .env. Turn it on only when your buckets are on your own network. While it is off, no workspace member can use a destination to reach services inside your network.

Setting Default Description
STORAGE_DRIVER local Where PDFs and uploaded files live: local (the storage-data volume on the server), s3 or azure.
OBJECT_STORAGE_BUCKET empty The S3 bucket, or the Azure container. Required for s3 and azure.
OBJECT_STORAGE_ENDPOINT empty The S3 endpoint URL, such as https://s3.eu-central-1.amazonaws.com. Empty uses Amazon S3’s default for the region.
OBJECT_STORAGE_REGION us-east-1 The S3 region.
OBJECT_STORAGE_ACCESS_KEY_ID empty The S3 access key ID. Required for s3.
OBJECT_STORAGE_SECRET_ACCESS_KEY empty The S3 secret access key. Required for s3.
OBJECT_STORAGE_FORCE_PATH_STYLE true Use path-style URLs (https://host/bucket/key). Set false for Amazon S3; keep true for MinIO and similar services.
OBJECT_STORAGE_PUBLIC_ENDPOINT empty For s3: the endpoint browsers and API clients reach, when it differs from OBJECT_STORAGE_ENDPOINT. Download and upload links use it.
AZURE_STORAGE_CONNECTION_STRING empty The storage account’s connection string, including its account key. Required for azure.
STUDIO_FILE_ORIGINS empty Origins Studio may load files from, separated by spaces, when files are in s3 or azure. Empty allows any https:// origin; an http:// origin must be listed.

Step by step for each service: file storage.

Memory values use Docker’s units, such as 512m and 2g. How to size them: memory limits.

Setting Default Description
POSTGRES_MEMORY_LIMIT 1g Memory limit of the postgres container.
REDIS_MEMORY_LIMIT 512m Memory limit of the redis container.
API_MEMORY_LIMIT 1536m Memory limit of the api container.
WORKER_MEMORY_LIMIT 2g Memory limit of each worker container.
SANDBOX_MEMORY_LIMIT 1g Memory limit of the sandbox container.
STUDIO_MEMORY_LIMIT 512m Memory limit of the studio container.
PROXY_MEMORY_LIMIT 256m Memory limit of the proxy container.
MONITORING_MEMORY_LIMIT 1g Memory limit of the prometheus container.
LOG_MAX_SIZE 10m Size at which each container’s log file is rotated.
LOG_MAX_FILE 5 Number of rotated log files each container keeps.
Setting Default Description
COMPOSE_PROFILES empty monitoring also runs Prometheus with the bundled alert rules.
PROMETHEUS_PORT 9090 The port of the Prometheus UI, published on the server’s 127.0.0.1 only.
PROMETHEUS_RETENTION 15d How long Prometheus keeps metrics.

See monitoring.

The API and worker read a few settings that compose.yaml does not pass on, so .env cannot set them. Most installations never need them. To set one, add it to compose.override.yaml next to compose.yaml (Compose reads that file automatically), then run docker compose up -d:

services:
api:
environment:
API_RATE_LIMIT_PER_MINUTE: "3000"
worker:
environment:
RENDER_MAX_PAGES: "100"

Keep compose.override.yaml when you upgrade.

Setting Default Description
API_RATE_LIMIT_PER_MINUTE 1200 Requests to /v1/*, and separately to /mcp, each client address may send per minute, across all API replicas. Over it: 429 RATE_LIMITED.

When to raise it: request rate limits.

These caps can only make the document limits stricter. Values above the defaults have no effect. A document over a cap fails with LIMIT_EXCEEDED.

Setting Default Range Description
RENDER_MAX_INPUT_BYTES 10485760 (10 MiB) 1 byte to 1 GiB Cap on a render’s resolved input, including its assets.
RENDER_MAX_PAGES 200 1 to 10000 Cap on the pages of one PDF.
RENDER_MAX_OUTPUT_BYTES 52428800 (50 MiB) 1 byte to 1 GiB Cap on the size of one PDF.

These set the memory one render may use, and you can raise them:

Setting Default Range Description
RENDER_MAX_WASM_BYTES 536870912 (512 MiB) 64 MiB to 4 GiB Layout memory one render may use. A render that needs more fails with LIMIT_EXCEEDED.
RENDER_MAX_HEAP_BYTES 268435456 (256 MiB) 64 MiB to 16 GiB JavaScript memory one render may use. A render that needs more fails.

If a large document fails with a memory error, raise RENDER_MAX_WASM_BYTES, and raise WORKER_MEMORY_LIMIT so it stays at least 512 MiB above the two render caps combined.