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).
How settings work
Section titled “How settings work”- Edit
.envon the server. - Run
docker compose up -din 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 incompose.override.yamlinstead.
Required settings
Section titled “Required settings”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.
Address, HTTPS and proxies
Section titled “Address, HTTPS and proxies”| 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.
Secrets and encryption
Section titled “Secrets and encryption”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. |
Rotate the data encryption key
Section titled “Rotate the data encryption key”- In
.env, move the current value ofDATA_ENCRYPTION_KEYtoDATA_ENCRYPTION_KEY_PREVIOUS. - Set
DATA_ENCRYPTION_KEYto a new value fromopenssl rand -base64 32, and store it with your other secrets. - Run
docker compose up -d. The API encrypts the stored secrets again with the new key as it starts, and retries every hour. - Once
docker compose logs apishows neitherWebhook secret encryption failednorSigning key encryption failed, removeDATA_ENCRYPTION_KEY_PREVIOUSand rundocker compose up -dagain.
Database backups taken before the rotation need the old key to restore; see the data encryption key.
Sign-in
Section titled “Sign-in”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.
Templates from PDFs
Section titled “Templates from PDFs”| 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.
Automated setup
Section titled “Automated setup”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 _. |
Rendering
Section titled “Rendering”| 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.
SCRIPT_ENV
Section titled “SCRIPT_ENV”For example, to add a watermark outside production:
SCRIPT_ENV=ADD_WATERMARK=true,ENVIRONMENT_NAME=stagingEvery 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.
Webhooks
Section titled “Webhooks”| 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.
Storage destinations
Section titled “Storage destinations”| 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.
File storage
Section titled “File storage”| 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 and logs
Section titled “Memory and logs”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. |
Monitoring
Section titled “Monitoring”| 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.
Advanced settings
Section titled “Advanced settings”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.
Worker
Section titled “Worker”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.