Back to blog

Docling Serve Configuration: Every DOCLING_SERVE_* Environment Variable

The complete docling-serve configuration reference, read from the v1.32.0 settings module. Every DOCLING_SERVE_* variable with type, default and effect, grouped by purpose, plus a Docker Compose file and the cu128 and cu130 image tags.

Docling Serve is configured almost entirely through environment variables prefixed DOCLING_SERVE_. There are close to 180 of them in the current release, and the published configuration doc lists roughly half. This page lists all of them.

Every name, type and default below is read from docling_serve/settings.py at the v1.32.0 tag, released September 1, 2026, with the engine settings cross-checked against docling-jobkit 3.5.0, which is the version that release pins. Where the doc and the source disagree, the source wins and the difference is called out.

This is the reference companion to two guides: Docling Serve: self-hosting the Docling REST API covers the endpoints, async jobs and timeouts, and Docling Docker: CPU/GPU images, Compose and model cache covers the container side. If you only want the Compose file, skip to it.

How Docling Serve reads configuration

The settings object is a pydantic-settings model with env_prefix="DOCLING_SERVE_". A field named max_sync_wait is therefore set by DOCLING_SERVE_MAX_SYNC_WAIT. Five things about the loader are worth knowing before the tables:

  1. Precedence is env var, then .env file, then config file, then defaults. The settings_customise_sources override in the source orders them init > env > dotenv > yaml_config > file_secret. A .env in the working directory is read automatically. DOCLING_SERVE_CONFIG_FILE points at a YAML or JSON file whose keys are the field names without the prefix (max_sync_wait: 300), and env vars override anything in it.
  2. Typos are silent. The model is declared with extra="allow", so DOCLING_SERVE_MAX_SYNC_WAIT_SECONDS does not raise; it is stored and ignored. Check /version or your logs rather than assuming a setting took.
  3. Empty string means unset. env_parse_none_str="" maps "" to None for optional fields. That is why -e DOCLING_SERVE_ARTIFACTS_PATH="" clears the artifacts path that the container image sets, which re-enables model auto-download.
  4. Lists are JSON unless a validator says otherwise. DOCLING_SERVE_CORS_ORIGINS must be a JSON array: '["https://app.example.com"]'. The ALLOWED_* allow-lists and ALLOWED_IMAGE_EXPORT_MODES have a validator that also accepts a comma-separated string. The CUSTOM_*_PRESETS settings must be JSON objects; a value that fails to parse becomes {} rather than an error.
  5. Booleans take the usual pydantic spellings. true, false, 1, 0, yes, no, on, off, case-insensitive.

The CLI exposes only --artifacts-path and --enable-ui at the app level. As of v1.32.0 the run command re-exports both as environment variables before starting Uvicorn, so they survive --reload and multi-worker spawns; every other app setting has no CLI flag at all. Configure through the environment and you never hit the gap.

Types below use Python notation from the source: str, int, float, bool, Path, list[str], dict. Optional[X] means unset by default.

Web server (UVICORN_*)

These are not DOCLING_SERVE_ variables but they live in the same settings module and you will need at least one of them.

VariableTypeDefaultEffect
UVICORN_HOSTstr0.0.0.0Bind address. docling-serve dev overrides to localhost
UVICORN_PORTint5001Bind port
UVICORN_WORKERSOptional[int]unset (Uvicorn uses 1)Server processes. Keep at 1; each process loads its own models, and the local engine’s task store is per-process
UVICORN_RELOADboolfalseCode auto-reload. dev sets it true
UVICORN_ROOT_PATHstr""ASGI root path when mounted under a prefix behind a proxy
UVICORN_PROXY_HEADERSbooltrueTrust X-Forwarded-* for client address
UVICORN_TIMEOUT_KEEP_ALIVEint60Keep-alive timeout in seconds
UVICORN_SSL_CERTFILEOptional[Path]unsetTLS certificate
UVICORN_SSL_KEYFILEOptional[Path]unsetTLS key
UVICORN_SSL_KEYFILE_PASSWORDOptional[str]unsetTLS key passphrase

Because the default bind is 0.0.0.0 and authentication is off, publish the container port to loopback (-p 127.0.0.1:5001:5001) unless a proxy with TLS sits in front.

Paths, models and startup

VariableTypeDefaultEffect
DOCLING_SERVE_CONFIG_FILEOptional[Path]unsetYAML or JSON file of settings. Env vars override it. Missing file raises at startup
DOCLING_SERVE_ARTIFACTS_PATHOptional[Path]unset in code; /opt/app-root/src/.cache/docling/models in the official imagesDirectory the model weights are loaded from. When set, a missing model is a hard error, not a download. Not to be confused with DOCLING_ARTIFACTS_PATH, which configures the Docling library itself
DOCLING_SERVE_STATIC_PATHOptional[Path]unsetDirectory of static assets for the API docs and UI, for offline deployments. Mounted at /static when the directory exists
DOCLING_SERVE_SCRATCH_PATHOptional[Path]unset (a temp dir is created)Working directory for results awaiting fetch. With SINGLE_USE_RESULTS=false this grows without bound
DOCLING_SERVE_LOAD_MODELS_AT_BOOTbooltrueWarm the converter cache at startup so /ready reflects real readiness. No-op under the rq engine, where models live in the workers
DOCLING_SERVE_OPTIONS_CACHE_SIZEint2How many DocumentConverter instances, each with its loaded models, to keep. Every distinct option combination a client sends occupies a slot
DOCLING_SERVE_ENABLE_UIboolfalseServe the Gradio playground at /ui. Install with the ui extra
DOCLING_SERVE_API_HOSTstrlocalhostHost the UI uses to reach the API. Only read by the Gradio UI; set it when the UI runs behind a different hostname

OPTIONS_CACHE_SIZE is the one people miss. Two clients that differ only in table_mode are two cache entries, and each holds a full model set in memory. Size it to the number of option combinations you actually serve, and watch memory when you raise it.

Request limits and policy

These are enforced server-side and return 422 when violated.

VariableTypeDefaultEffect
DOCLING_SERVE_MAX_DOCUMENT_TIMEOUTfloat604800 (7 days)Ceiling for document_timeout. Also the value substituted when a request omits it. Set this; a bad scan can otherwise hold a worker for a week
DOCLING_SERVE_MAX_NUM_PAGESintsys.maxsize (unbounded)Reject documents with more pages than this
DOCLING_SERVE_MAX_FILE_SIZEintsys.maxsize (unbounded)Reject inputs larger than this many bytes
DOCLING_SERVE_MAX_SOURCES_PER_REQUESTint3Maximum sources entries, or files on the multipart endpoint, per request. A fourth is rejected
DOCLING_SERVE_MAX_IMAGES_SCALEfloat2.0Ceiling for images_scale. Values above it are rejected, not clamped
DOCLING_SERVE_ALLOWED_IMAGE_EXPORT_MODESOptional[list[str]]unset (all three)Restrict image_export_mode to a subset of placeholder, referenced, embedded. Unknown names are dropped silently
DOCLING_SERVE_ALLOWED_SOURCE_TYPESOptional[list[str]]unset (built-in kinds)Allow-list of source kinds. Plugin-registered sources must be listed explicitly; local_path is never available remotely
DOCLING_SERVE_ALLOWED_TARGET_TYPESOptional[list[str]]unset (built-in kinds)Allow-list of target kinds, same rules as sources

MAX_FILE_SIZE and MAX_NUM_PAGES are unbounded by default. Set both before you accept untrusted uploads.

Sync endpoints and results

VariableTypeDefaultEffect
DOCLING_SERVE_MAX_SYNC_WAITint120Seconds a sync endpoint (/v1/convert/source, /v1/convert/file) waits before returning 504. The task is not cancelled when it fires
DOCLING_SERVE_SYNC_POLL_INTERVALint2Seconds between the sync handler’s checks on its own task
DOCLING_SERVE_SINGLE_USE_RESULTSbooltrueSchedule a result for removal once it has been fetched. false keeps results in the scratch directory indefinitely
DOCLING_SERVE_RESULT_REMOVAL_DELAYint300Grace period in seconds between the first fetch and removal, when single-use is on

Raising MAX_SYNC_WAIT is the last resort, not the first. The sync handler enqueues the task and polls it; on timeout it raises 504 and leaves the conversion running, with a TODO: abort task! at that point in the source. The Serve guide’s timeout section walks through the order of fixes.

Security and exposure

VariableTypeDefaultEffect
DOCLING_SERVE_API_KEYstr"" (auth off)Shared secret. When set, every API route requires the X-Api-Key header, including status polls and result fetches. Websockets take ?api_key=. Health and metrics endpoints stay open
DOCLING_SERVE_CORS_ORIGINSlist[str]["*"]Allowed cross-origin origins. JSON array only
DOCLING_SERVE_CORS_METHODSlist[str]["*"]Allowed cross-origin methods
DOCLING_SERVE_CORS_HEADERSlist[str]["*"]Allowed cross-origin request headers
DOCLING_SERVE_SHOW_VERSION_INFObooltrue/version returns package versions. false makes it 403
DOCLING_SERVE_ENABLE_MANAGEMENT_ENDPOINTSboolfalseEnable /v1/memory/* statistics. Otherwise 403
DOCLING_SERVE_DEBUG_ERROR_DETAILSboolfalseReturn raw internal exception text in HTTP and task errors. Off, infrastructure-origin errors are sanitised
DOCLING_SERVE_ENABLE_REMOTE_SERVICESboolfalseAllow pipeline components to make outbound calls, for example a VLM served over an API. Off keeps processing local
DOCLING_SERVE_ALLOW_EXTERNAL_PLUGINSboolfalseLoad third-party connector and model plugins. The packages must be installed in every API and worker process

There is no per-tenant credential, rate limit or quota. If you need any of that, it goes in a proxy in front of Docling Serve.

Logging

VariableTypeDefaultEffect
DOCLING_SERVE_LOG_LEVELOptional[WARNING|INFO|DEBUG]unset (resolves to WARNING)Verbosity. Case-insensitive. -v / -vv on the CLI override it
DOCLING_SERVE_LOG_FORMATtext|jsontextjson emits one structured object per line; use it for anything that aggregates logs
DOCLING_SERVE_LOG_HEADER_PREFIXstrX-Docling-Log-Request headers matching this prefix are stripped of the prefix and attached to every log line for that request. X-Docling-Log-RequestID: abc becomes "RequestID": "abc"

The header propagation is the cheapest request tracing available here, and it works under the local engine without any extra setup.

Pipeline batching

These flow straight into docling-jobkit’s DoclingConverterManagerConfig. They are all unset by default, which means the library’s own defaults apply.

VariableTypeDefaultEffect
DOCLING_SERVE_QUEUE_MAX_SIZEOptional[int]unsetSize of the page queue between pipeline stages, so the upper bound on pages open at once
DOCLING_SERVE_OCR_BATCH_SIZEOptional[int]unsetPages per batch in the OCR stage
DOCLING_SERVE_LAYOUT_BATCH_SIZEOptional[int]unsetPages per batch in layout detection
DOCLING_SERVE_TABLE_BATCH_SIZEOptional[int]unsetPages per batch in table structure
DOCLING_SERVE_BATCH_POLLING_INTERVAL_SECONDSOptional[float]unsetHow long a stage waits to gather pages before starting a batch

Two Docling library variables belong next to them, because they decide how many CPU threads and which device the conversion uses:

VariableDefaultEffect
DOCLING_NUM_THREADS4Torch CPU threads inside conversion. Set at or below the container CPU limit
DOCLING_DEVICEunset (auto)cpu, cuda, cuda:N, or mps
DOCLING_PERF_PAGE_BATCH_SIZE4Pages processed per batch in the library
DOCLING_PERF_ELEMENTS_BATCH_SIZE8Document elements per batch during enrichment
OMP_NUM_THREADS4 in the official imagesOpenMP thread pool

Async engine

DOCLING_SERVE_ENG_KIND picks which engine runs the async endpoints and, under local, the sync ones too.

VariableTypeDefaultEffect
DOCLING_SERVE_ENG_KINDlocal|rq|raylocallocal runs conversions in-process. rq dispatches to Redis-backed RQ workers. ray dispatches to a Ray cluster with Redis for state

Local engine (ENG_LOC_*)

VariableTypeDefaultEffect
DOCLING_SERVE_ENG_LOC_NUM_WORKERSint2Worker threads pulling tasks from the in-process queue, so concurrent conversions per process
DOCLING_SERVE_ENG_LOC_SHARE_MODELSboolfalseShare one model set across those threads. Off, each worker thread allocates its own copy

SHARE_MODELS=true is the easiest memory saving available. The default allocates a full model graph per worker, so two workers cost roughly twice the memory of one. Pending tasks under local live in the process and are lost on restart.

RQ engine (ENG_RQ_*)

Set ENG_KIND=rq on the API containers and the workers alike, run workers with docling-serve rq-worker, and the API stops converting entirely. The Redis URL is validated at startup; the process refuses to boot without it.

VariableTypeDefaultEffect
DOCLING_SERVE_ENG_RQ_REDIS_URLstr"" (required)Redis connection URL, e.g. redis://redis:6379/
DOCLING_SERVE_ENG_RQ_QUEUE_NAMEstrconvertQueue name shared by API instances and workers
DOCLING_SERVE_ENG_RQ_RESULTS_PREFIXstrdocling:resultsKey prefix for stored results
DOCLING_SERVE_ENG_RQ_SUB_CHANNELstrdocling:updatesPub/sub channel workers use to report status
DOCLING_SERVE_ENG_RQ_RESULTS_TTLint14400 (4 h)Seconds a successful job’s result is kept in Redis
DOCLING_SERVE_ENG_RQ_FAILURE_TTLint14400 (4 h)Seconds a failed job is kept. Not in the configuration doc
DOCLING_SERVE_ENG_RQ_JOB_TIMEOUTint14400 (4 h)Maximum runtime per job before the worker aborts it. Baked in at enqueue time, so set it on the API side. -1 disables. 0 does not: RQ reads 0 as unset and applies its own 180-second default
DOCLING_SERVE_ENG_RQ_REDIS_MAX_CONNECTIONSint50Connection pool size. 50 covers 1–4 workers, 100 for 5–10, 150–200 beyond
DOCLING_SERVE_ENG_RQ_REDIS_SOCKET_TIMEOUTOptional[float]unsetSocket timeout for Redis operations
DOCLING_SERVE_ENG_RQ_REDIS_SOCKET_CONNECT_TIMEOUTOptional[float]unsetSocket connect timeout
DOCLING_SERVE_ENG_RQ_REDIS_GATE_CONCURRENCYOptional[int]unset (MAX_CONNECTIONS − RESERVED, min 1)Concurrent caller-facing Redis operations allowed. Not in the configuration doc
DOCLING_SERVE_ENG_RQ_REDIS_GATE_RESERVED_CONNECTIONSint10Pool connections held back for background work when computing the gate default
DOCLING_SERVE_ENG_RQ_REDIS_GATE_WAIT_TIMEOUTfloat0.25Seconds a submit or result fetch waits for a gate slot before failing
DOCLING_SERVE_ENG_RQ_REDIS_GATE_STATUS_POLL_WAIT_TIMEOUTfloat5.0Same, for status polls
DOCLING_SERVE_ENG_RQ_ZOMBIE_REAPER_INTERVALfloat300.0Seconds between sweeps of the API’s task-tracking table
DOCLING_SERVE_ENG_RQ_ZOMBIE_REAPER_MAX_AGEfloat3600.0Tracked tasks older than this with no live job are dropped from the API’s view

The gate settings exist so that a burst of status polls cannot exhaust the Redis pool and starve the background listener; the four GATE_* values and the two ZOMBIE_REAPER_* values are undocumented upstream and were read from the jobkit RQOrchestratorConfig. Leave them alone unless you see gate-timeout errors in the logs.

Ray engine (ENG_RAY_*)

The Ray engine requires both a Redis URL and a Ray address, and refuses to start without either. It is also the largest settings surface in the module and is not covered by the upstream configuration doc at all. Defaults below are Docling Serve’s own, which in a few places differ from jobkit’s.

Connection and storage:

VariableTypeDefaultEffect
DOCLING_SERVE_ENG_RAY_REDIS_URLstr"" (required)Redis URL. Standard, Sentinel (redis+sentinel://) and Cluster (?cluster=true) forms are accepted
DOCLING_SERVE_ENG_RAY_ADDRESSstr"" (required)Ray cluster address. auto or local are passed to Ray as “auto-detect or start local”
DOCLING_SERVE_ENG_RAY_NAMESPACEstrdoclingRay namespace for isolation
DOCLING_SERVE_ENG_RAY_RUNTIME_ENVOptional[dict]unsetRay runtime environment, as JSON
DOCLING_SERVE_ENG_RAY_ENABLE_MTLSboolfalsemTLS to the Ray cluster
DOCLING_SERVE_ENG_RAY_CLUSTER_NAMEOptional[str]unsetCluster name for certificate generation; required when mTLS is on
DOCLING_SERVE_ENG_RAY_REDIS_MAX_CONNECTIONSint50Pool size
DOCLING_SERVE_ENG_RAY_REDIS_SOCKET_TIMEOUTOptional[float]unsetSocket timeout
DOCLING_SERVE_ENG_RAY_REDIS_SOCKET_CONNECT_TIMEOUTOptional[float]unsetSocket connect timeout
DOCLING_SERVE_ENG_RAY_REDIS_GATE_CONCURRENCYOptional[int]unsetConcurrent caller-facing Redis operations
DOCLING_SERVE_ENG_RAY_REDIS_GATE_RESERVED_CONNECTIONSint10Connections held back for internal work
DOCLING_SERVE_ENG_RAY_REDIS_GATE_WAIT_TIMEOUTfloat0.25Gate wait for submits and fetches
DOCLING_SERVE_ENG_RAY_REDIS_GATE_STATUS_POLL_WAIT_TIMEOUTfloat5.0Gate wait for status polls
DOCLING_SERVE_ENG_RAY_REDIS_OPERATION_TIMEOUTfloat30.0Timeout per Redis operation
DOCLING_SERVE_ENG_RAY_RESULTS_TTLint14400 (4 h)Result lifetime in Redis
DOCLING_SERVE_ENG_RAY_RESULTS_PREFIXstrdocling:ray:resultsResult key prefix
DOCLING_SERVE_ENG_RAY_SUB_CHANNELstrdocling:ray:updatesPub/sub channel for task updates
DOCLING_SERVE_ENG_RAY_SCRATCH_DIROptional[Path]unset (falls back to SCRATCH_PATH)Scratch directory for the Ray orchestrator
DOCLING_SERVE_ENG_RAY_LOG_LEVELstrINFOLog level for the Ray orchestrator

Fairness and per-tenant limits. Tenants are identified by a request header:

VariableTypeDefaultEffect
DOCLING_SERVE_ENG_RAY_TENANT_ID_HEADERstrX-Tenant-IdHeader the API reads to attribute a request to a tenant
DOCLING_SERVE_ENG_RAY_MAX_CONCURRENT_TASKSint5Tasks in flight per tenant
DOCLING_SERVE_ENG_RAY_MAX_QUEUED_TASKSOptional[int]unset (unlimited)Queued tasks per tenant
DOCLING_SERVE_ENG_RAY_ENABLE_QUEUE_LIMIT_REJECTIONboolfalseReturn 429 when the queue limit is hit, instead of waiting
DOCLING_SERVE_ENG_RAY_MAX_DOCUMENTSOptional[int]unset (unlimited)Documents in processing per tenant
DOCLING_SERVE_ENG_RAY_ENABLE_DOCUMENT_LIMITSboolfalseEnforce MAX_DOCUMENTS
DOCLING_SERVE_ENG_RAY_DISPATCHER_INTERVALfloat30.0Slow-path resync cadence; the dispatcher also wakes immediately on new work
DOCLING_SERVE_ENG_RAY_SUPERVISOR_POLL_INTERVALfloat5.0Seconds between supervisor health checks

Autoscaling and resources:

VariableTypeDefaultEffect
DOCLING_SERVE_ENG_RAY_MIN_ACTORSint1Converter replica lower bound
DOCLING_SERVE_ENG_RAY_MAX_ACTORSint10Converter replica upper bound
DOCLING_SERVE_ENG_RAY_TARGET_REQUESTS_PER_REPLICAfloat > 01.0Autoscaling target of concurrent requests per replica
DOCLING_SERVE_ENG_RAY_MAX_ONGOING_REQUESTS_PER_REPLICAOptional[int]unset (follows the target)Hard cap on in-flight requests per replica
DOCLING_SERVE_ENG_RAY_CONVERTER_MAX_REPLICAS_PER_NODEOptional[int]unset (no cap)Converter replicas per Ray node, 1–100
DOCLING_SERVE_ENG_RAY_UPSCALE_DELAY_Sfloat30.0Wait before scaling up
DOCLING_SERVE_ENG_RAY_DOWNSCALE_DELAY_Sfloat600.0Wait before scaling down
DOCLING_SERVE_ENG_RAY_GRACEFUL_SHUTDOWN_WAIT_LOOP_SOptional[float]unset (Ray Serve default)Seconds between drain checks during replica shutdown
DOCLING_SERVE_ENG_RAY_GRACEFUL_SHUTDOWN_TIMEOUT_SOptional[float]unset (Ray Serve default)Maximum drain wait before a replica is killed
DOCLING_SERVE_ENG_RAY_CONVERTER_ACTOR_NUM_CPUSfloat1.0CPU request per converter replica. ENG_RAY_NUM_CPUS_PER_ACTOR is accepted as a deprecated alias and logs a warning
DOCLING_SERVE_ENG_RAY_CONVERTER_ACTOR_MEMORY_REQUESTOptional[str]unsetMemory request per converter replica, e.g. 8GB or 8Gi. ENG_RAY_MEMORY_LIMIT_PER_ACTOR is the deprecated alias
DOCLING_SERVE_ENG_RAY_DISPATCHER_NUM_CPUSfloat0.25CPU request for the dispatcher actor
DOCLING_SERVE_ENG_RAY_DISPATCHER_MEMORY_REQUESTOptional[str]unsetMemory request for the dispatcher actor
DOCLING_SERVE_ENG_RAY_OBJECT_STORE_MEMORYOptional[str]unsetRay object store size
DOCLING_SERVE_ENG_RAY_ENABLE_OOM_PROTECTIONbooltrueMonitor actor memory and act before the OS OOM killer does
DOCLING_SERVE_ENG_RAY_MEMORY_WARNING_THRESHOLDfloat0.9Fraction of the memory request at which warnings start

Page-slice fan-out and coordinators. With fan-out enabled, a large PDF is split into page ranges converted in parallel and reassembled by a coordinator replica:

VariableTypeDefaultEffect
DOCLING_SERVE_ENG_RAY_ENABLE_PDF_PAGE_SLICE_FANOUTboolfalseSplit eligible PDFs into page slices
DOCLING_SERVE_ENG_RAY_MAX_PAGE_SLICE_SIZEint32Pages per slice. Docling Serve’s default; jobkit’s own is 10
DOCLING_SERVE_ENG_RAY_MAX_PAGE_SLICE_PARALLELISMOptional[int]unset (falls back to MAX_CONCURRENT_TASKS)Docling Serve still computes and passes this, but jobkit 3.5.0 marks it deprecated and ignored; fan-out concurrency is governed per tenant by MAX_CONCURRENT_TASKS
DOCLING_SERVE_ENG_RAY_COORDINATOR_MIN_ACTORSOptional[int]unset (MIN_ACTORS)Coordinator replica lower bound
DOCLING_SERVE_ENG_RAY_COORDINATOR_MAX_ACTORSOptional[int]unset (MAX_ACTORS)Coordinator replica upper bound
DOCLING_SERVE_ENG_RAY_COORDINATOR_TARGET_REQUESTS_PER_REPLICAOptional[float > 0]unsetCoordinator autoscaling target
DOCLING_SERVE_ENG_RAY_COORDINATOR_MAX_ONGOING_REQUESTS_PER_REPLICAint8Hard cap on in-flight requests per coordinator
DOCLING_SERVE_ENG_RAY_COORDINATOR_MAX_REPLICAS_PER_NODEOptional[int]unset (no cap)Coordinator replicas per node
DOCLING_SERVE_ENG_RAY_COORDINATOR_ACTOR_NUM_CPUSfloat0.25CPU request per coordinator
DOCLING_SERVE_ENG_RAY_COORDINATOR_ACTOR_MEMORY_REQUESTOptional[str]unsetMemory request per coordinator

Retries, timeouts and health:

VariableTypeDefaultEffect
DOCLING_SERVE_ENG_RAY_MAX_TASK_RETRIESint3Retries for a failed task
DOCLING_SERVE_ENG_RAY_RETRY_DELAYfloat5.0Seconds between task retries
DOCLING_SERVE_ENG_RAY_MAX_DOCUMENT_RETRIESint2Retries per document within a task
DOCLING_SERVE_ENG_RAY_DISPATCHER_MAX_RESTARTSint-1 (unlimited)Dispatcher actor restarts
DOCLING_SERVE_ENG_RAY_DISPATCHER_MAX_TASK_RETRIESint3Ray-level retries for dispatcher operations
DOCLING_SERVE_ENG_RAY_TASK_TIMEOUTOptional[float]3600.0Maximum seconds per task; empty disables
DOCLING_SERVE_ENG_RAY_DOCUMENT_TIMEOUTOptional[float]300.0Maximum seconds per document; empty disables. Note this is five minutes, far below the API-level MAX_DOCUMENT_TIMEOUT
DOCLING_SERVE_ENG_RAY_DISPATCHER_RPC_TIMEOUTfloat5.0Timeout for one dispatcher health-check RPC
DOCLING_SERVE_ENG_RAY_LIVENESS_FAIL_AFTERfloat90.0Seconds of continuous unhealthiness before /livez fails so Kubernetes restarts the pod
DOCLING_SERVE_ENG_RAY_ENABLE_HEARTBEATbooltrueDispatcher heartbeat monitoring

If you do not already operate Ray, run RQ. The Ray engine is designed for multi-tenant clusters and its defaults assume one.

Artifact storage

Enables the PresignedUrlTarget, where the server writes converted output to object storage and returns a presigned URL instead of the document body. Off by default.

VariableTypeDefaultEffect
DOCLING_SERVE_ARTIFACT_STORAGE_ENABLEDboolfalseTurn managed artifact storage on
DOCLING_SERVE_ARTIFACT_STORAGE_BACKENDs3|azures3Which backend
DOCLING_SERVE_ARTIFACT_STORAGE_ENDPOINTstr""S3 endpoint host, without protocol
DOCLING_SERVE_ARTIFACT_STORAGE_VERIFY_SSLbooltrueVerify the S3 endpoint’s TLS certificate. false for plain-HTTP or self-signed MinIO
DOCLING_SERVE_ARTIFACT_STORAGE_BUCKETstr""S3 bucket
DOCLING_SERVE_ARTIFACT_STORAGE_ACCESS_KEYstr""S3 access key
DOCLING_SERVE_ARTIFACT_STORAGE_SECRET_KEYstr""S3 secret key
DOCLING_SERVE_ARTIFACT_STORAGE_KEY_PREFIXstrconverted/S3 object key prefix
DOCLING_SERVE_ARTIFACT_STORAGE_AZURE_CONNECTION_STRINGstr""Azure connection string. Must carry AccountName and AccountKey; managed identity and SAS-only strings are not supported
DOCLING_SERVE_ARTIFACT_STORAGE_AZURE_CONTAINERstr""Azure Blob container
DOCLING_SERVE_ARTIFACT_STORAGE_AZURE_ACCOUNT_NAMEstr""Azure account name; must match the connection string
DOCLING_SERVE_ARTIFACT_STORAGE_AZURE_BLOB_PREFIXstrconverted/Azure blob name prefix
DOCLING_SERVE_ARTIFACT_STORAGE_PRESIGN_TTL_SECONDSint3600Lifetime of the returned presigned or SAS URL. Documented range 60–604800

With the Azure backend, the connection string, container and account name are all checked at startup and a missing one raises with the variable names in the message.

Model presets and allow-lists

These decide which models a client may ask for and what "default" means. They pass through to the converter manager unchanged. The pattern repeats for each model family: a DEFAULT_* preset, an optional ALLOWED_* list that restricts clients to a subset, a CUSTOM_* JSON object that registers new presets, and an ALLOW_CUSTOM_*_CONFIG flag that lets clients send a fully custom configuration instead of a preset name.

ALLOWED_* lists accept a JSON array or a comma-separated string. CUSTOM_* presets must be JSON objects mapping a preset id to that family’s options.

VLM pipeline:

VariableTypeDefault
DOCLING_SERVE_DEFAULT_VLM_PRESETstrgranite_docling
DOCLING_SERVE_ALLOWED_VLM_PRESETSOptional[list[str]]unset (all)
DOCLING_SERVE_CUSTOM_VLM_PRESETSdict{}
DOCLING_SERVE_ALLOWED_VLM_ENGINESOptional[list[str]]unset (all)
DOCLING_SERVE_ALLOW_CUSTOM_VLM_CONFIGboolfalse

Picture description:

VariableTypeDefault
DOCLING_SERVE_DEFAULT_PICTURE_DESCRIPTION_PRESETstrsmolvlm
DOCLING_SERVE_ALLOWED_PICTURE_DESCRIPTION_PRESETSOptional[list[str]]unset (all)
DOCLING_SERVE_CUSTOM_PICTURE_DESCRIPTION_PRESETSdict{}
DOCLING_SERVE_ALLOWED_PICTURE_DESCRIPTION_ENGINESOptional[list[str]]unset (all)
DOCLING_SERVE_ALLOW_CUSTOM_PICTURE_DESCRIPTION_CONFIGboolfalse

Code and formula enrichment:

VariableTypeDefault
DOCLING_SERVE_DEFAULT_CODE_FORMULA_PRESETstrdefault
DOCLING_SERVE_ALLOWED_CODE_FORMULA_PRESETSOptional[list[str]]unset (all)
DOCLING_SERVE_CUSTOM_CODE_FORMULA_PRESETSdict{}
DOCLING_SERVE_ALLOWED_CODE_FORMULA_ENGINESOptional[list[str]]unset (all)
DOCLING_SERVE_ALLOW_CUSTOM_CODE_FORMULA_CONFIGboolfalse

Picture classification:

VariableTypeDefault
DOCLING_SERVE_DEFAULT_PICTURE_CLASSIFICATION_PRESETstrdocument_figure_classifier_v2
DOCLING_SERVE_ALLOWED_PICTURE_CLASSIFICATION_PRESETSOptional[list[str]]unset (all)
DOCLING_SERVE_CUSTOM_PICTURE_CLASSIFICATION_PRESETSdict{}
DOCLING_SERVE_ALLOW_CUSTOM_PICTURE_CLASSIFICATION_CONFIGboolfalse

Table structure. Table and layout have both a kind (which implementation) and a preset (which configuration of it):

VariableTypeDefault
DOCLING_SERVE_DEFAULT_TABLE_STRUCTURE_KINDstrdocling_tableformer
DOCLING_SERVE_ALLOWED_TABLE_STRUCTURE_KINDSOptional[list[str]]unset (all; the default kind is always allowed)
DOCLING_SERVE_DEFAULT_TABLE_STRUCTURE_PRESETstrtableformer_v1_accurate
DOCLING_SERVE_ALLOWED_TABLE_STRUCTURE_PRESETSOptional[list[str]]unset (all)
DOCLING_SERVE_CUSTOM_TABLE_STRUCTURE_PRESETSdict{}
DOCLING_SERVE_ALLOW_CUSTOM_TABLE_STRUCTURE_CONFIGboolfalse

Layout:

VariableTypeDefault
DOCLING_SERVE_DEFAULT_LAYOUT_KINDstrdocling_layout_default
DOCLING_SERVE_ALLOWED_LAYOUT_KINDSOptional[list[str]]unset (all; the default kind is always allowed)
DOCLING_SERVE_DEFAULT_LAYOUT_PRESETstrdocling_layout_default
DOCLING_SERVE_ALLOWED_LAYOUT_PRESETSOptional[list[str]]unset (all)
DOCLING_SERVE_CUSTOM_LAYOUT_PRESETSdict{}
DOCLING_SERVE_ALLOW_CUSTOM_LAYOUT_CONFIGboolfalse

OCR. Not in the upstream configuration doc:

VariableTypeDefault
DOCLING_SERVE_DEFAULT_OCR_KINDstrauto
DOCLING_SERVE_DEFAULT_OCR_PRESETstrauto
DOCLING_SERVE_ALLOWED_OCR_KINDSOptional[list[str]]unset (all)
DOCLING_SERVE_ALLOWED_OCR_PRESETSOptional[list[str]]unset (all)
DOCLING_SERVE_CUSTOM_OCR_PRESETSdict{}
DOCLING_SERVE_ALLOW_CUSTOM_OCR_CONFIGboolfalse

Chunking, for the /v1/chunk/* endpoints. Also undocumented upstream:

VariableTypeDefault
DOCLING_SERVE_DEFAULT_CHUNKING_PRESETstrgranite_embedding_278m
DOCLING_SERVE_ALLOWED_CHUNKING_PRESETSOptional[list[str]]unset (all)
DOCLING_SERVE_CUSTOM_CHUNKING_PRESETSdict{}

The four ALLOW_CUSTOM_*_CONFIG flags for table structure, layout, picture classification and OCR are wired through orchestrator_factory.py but absent from the configuration doc, which lists only the VLM, picture description and code/formula ones.

Telemetry

VariableTypeDefaultEffect
DOCLING_SERVE_OTEL_ENABLE_METRICSbooltrueCollect OpenTelemetry metrics
DOCLING_SERVE_OTEL_ENABLE_TRACESboolfalseCollect traces. Needs OTEL_EXPORTER_OTLP_ENDPOINT. Under RQ this also wraps the queue so trace context reaches the workers
DOCLING_SERVE_OTEL_ENABLE_PROMETHEUSbooltrueServe /metrics in Prometheus format
DOCLING_SERVE_OTEL_ENABLE_OTLP_METRICSboolfalseAlso export metrics over OTLP
DOCLING_SERVE_OTEL_SERVICE_NAMEstrdocling-serveService name on emitted telemetry
DOCLING_SERVE_METRICS_PORTOptional[int]unsetServe /metrics on a separate port instead of the API port. A bind failure raises at startup

OTEL_EXPORTER_OTLP_ENDPOINT is a standard OpenTelemetry variable, not a DOCLING_SERVE_ one, but traces and OTLP metrics go nowhere without it.

Docker Compose example

A single-container Compose file that sets the variables which matter before real traffic arrives. Models ship inside the official image, so there is no volume and no download step. The Docker guide defends each choice; this version adds an API key and a CORS restriction.

services:
  docling:
    image: quay.io/docling-project/docling-serve-cpu:v1.32.0
    ports:
      - '127.0.0.1:5001:5001'
    environment:
      # Server
      UVICORN_WORKERS: '1'
      DOCLING_NUM_THREADS: '4'
      OMP_NUM_THREADS: '4'
      # Local engine
      DOCLING_SERVE_ENG_LOC_NUM_WORKERS: '2'
      DOCLING_SERVE_ENG_LOC_SHARE_MODELS: 'true'
      # Limits
      DOCLING_SERVE_MAX_DOCUMENT_TIMEOUT: '1800'
      DOCLING_SERVE_MAX_SYNC_WAIT: '120'
      DOCLING_SERVE_MAX_FILE_SIZE: '52428800'
      DOCLING_SERVE_MAX_NUM_PAGES: '200'
      DOCLING_SERVE_MAX_SOURCES_PER_REQUEST: '3'
      # Exposure
      DOCLING_SERVE_API_KEY: 'replace-me'
      DOCLING_SERVE_CORS_ORIGINS: '["https://app.example.com"]'
      DOCLING_SERVE_SHOW_VERSION_INFO: 'false'
      # Logging
      DOCLING_SERVE_LOG_LEVEL: 'INFO'
      DOCLING_SERVE_LOG_FORMAT: 'json'
    healthcheck:
      test:
        - CMD
        - python
        - -c
        - "import urllib.request; urllib.request.urlopen('http://127.0.0.1:5001/ready', timeout=5).read()"
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 180s
    stop_grace_period: 2m
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 12G

GPU images: cu128 and cu130

Four official images exist, mirrored on quay.io/docling-project/ and ghcr.io/docling-project/:

ImageTorch buildArch
docling-servePyPI torch, CUDA libraries includedamd64, arm64
docling-serve-cpuCPU-only torchamd64, arm64
docling-serve-cu128CUDA 12.8 torchamd64
docling-serve-cu130CUDA 13.0 torchamd64, arm64

The CUDA images are deliberately not tagged latest, only with explicit versions and main, so that a deprecated CUDA build cannot arrive through a floating tag. Pin the version and match the tag to your host driver: a cu128 image needs a driver that supports the CUDA 12.8 runtime, cu130 needs 13.0.

As a Compose override on the file above:

services:
  docling:
    image: quay.io/docling-project/docling-serve-cu128:v1.32.0
    environment:
      DOCLING_DEVICE: 'cuda'
      NVIDIA_VISIBLE_DEVICES: 'all'
      NVIDIA_DRIVER_CAPABILITIES: 'compute,utility'
    runtime: nvidia

Swap in docling-serve-cu130:v1.32.0 for a CUDA 13.0 host. DOCLING_DEVICE is a Docling library variable, not a Docling Serve one, and it is the thing that actually moves the layout and table models onto the GPU. OCR through RapidOCR’s ONNX backend can still fall back to CPU silently; the Serve guide covers the check.

Scaling out with RQ

Three services: Redis, the API, and workers built from the same image with a different command. ENG_RQ_JOB_TIMEOUT sits on the API service because it is baked into each job at enqueue time.

services:
  redis:
    image: redis:7-alpine
    command: ['redis-server', '--appendonly', 'yes']
    volumes:
      - redis-data:/data

  docling-api:
    image: quay.io/docling-project/docling-serve-cpu:v1.32.0
    ports:
      - '127.0.0.1:5001:5001'
    environment:
      DOCLING_SERVE_ENG_KIND: 'rq'
      DOCLING_SERVE_ENG_RQ_REDIS_URL: 'redis://redis:6379/'
      DOCLING_SERVE_ENG_RQ_JOB_TIMEOUT: '3600'
      DOCLING_SERVE_MAX_DOCUMENT_TIMEOUT: '1800'
      DOCLING_SERVE_API_KEY: 'replace-me'
      DOCLING_SERVE_LOG_FORMAT: 'json'
    depends_on:
      - redis

  docling-worker:
    image: quay.io/docling-project/docling-serve-cpu:v1.32.0
    command: ['docling-serve', 'rq-worker']
    environment:
      DOCLING_SERVE_ENG_KIND: 'rq'
      DOCLING_SERVE_ENG_RQ_REDIS_URL: 'redis://redis:6379/'
      DOCLING_NUM_THREADS: '4'
      OMP_NUM_THREADS: '4'
      DOCLING_SERVE_LOG_FORMAT: 'json'
    depends_on:
      - redis
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 12G

volumes:
  redis-data:

Under RQ the API’s /ready gates on its queue processor and Redis connection, not on whether a worker has loaded its models. Probe the workers separately. The project’s Kubernetes manifest is the same shape.

Self-hosting versus a hosted Docling API

Everything on this page is the cost of owning Docling Serve: a model cache, thread caps, an engine choice, Redis, timeouts at three layers, and a readiness probe that means what it says. That is a fair trade when you need formats beyond PDF, control over OCR, a pinned Docling version, or processing that never leaves your network. It is a poor trade when you wanted Markdown out of a PDF and nothing else.

If the second one is you, Parsebridge runs Docling as a managed API. It is our own product, so here is the honest scope: PDF in, Markdown out, through our request shape rather than Docling Serve’s. No DOCLING_SERVE_ variables, no Compose file, no /ready probe:

curl -X POST https://api.parsebridge.com/v1/parse/url \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/document.pdf"}'

If you already run Docling Serve and it is behaving, stay there. Same parser either way.


Names, types and defaults above were read from docling_serve/settings.py, orchestrator_factory.py and policy.py at the v1.32.0 tag, with engine semantics from the docling-jobkit 3.5.0 RQ and Ray orchestrator configs. Descriptions that the upstream configuration doc does not provide were inferred from how the value is used in code, and are marked as such. The Compose files are source-reviewed against that release, not runtime-tested here.