Server Configuration

This page covers a model server, paddock-runner: the process that holds one model on the GPU. It is configured through CLI flags, environment variables, and a TOML config file, layered in a strict precedence order. For the manager's own verbs and flags, see the CLI page.

paddock serve writes one of these config files per endpoint at ~/paddock/servers/<port>.toml, so anything below can be set by hand there and started with paddock start, or run directly with paddock-runner --config.

Precedence

Every setting can come from four places. Higher layers win:

  1. CLI flags
  2. PADDOCK_* environment variables
  3. paddock.toml
  4. Built-in defaults

The config file is paddock.toml in the working directory, or a path given with --config <path> (short form -c). Unknown keys and malformed files are errors that stop startup. The startup banner shows where each value came from, and a commented reference file, paddock.example.toml, ships with the release.

CLI Arguments

FlagDescriptionDefault
--config, -c <PATH>Path to TOML config filepaddock.toml (CWD)
--host <ADDR>Address to bind the HTTP API to0.0.0.0
--port <PORT>Port to bind the HTTP API to11540
--model, -m <MODEL>GGUF path or catalog model id to load and serve at startup. Unset = start with no model and serve /v1/models only.-
--model-dir <PATH>Directory scanned for GGUF files (repeatable)~/paddock/models
--device <DEV>Compute device: cuda or cpu. The cpu device runs reference paths for a subset of models and is not a serving path.cuda
--kernel-pack <PATH>Path to the CUDA kernel pack. Required with --device cuda.-
--max-ctx <N>Max context length (KV cache) for the served model4096
--max-batch <N>Continuous-batching width: sequences batched per step (1 = serial loop). Lower it on a tight card to free memory.32
--mmproj <PATH>Vision tower GGUF (mmproj) for multimodal models; enables image input-
--mtp <PATH>Separate MTP drafter GGUF for speculative decoding (used by Gemma 4)-
--max-output-tokens <N>Default max output tokens per reply when a request does not specify oneunset
--api-key <KEY>Bearer key for API auth (see Authentication)-

Environment Variables

Every CLI flag has a corresponding environment variable, and two PDF settings exist only as environment variables or TOML keys:

VariableCLI Equivalent
PADDOCK_HOST--host
PADDOCK_PORT--port
PADDOCK_MODEL--model
PADDOCK_MODEL_DIRS--model-dir (comma-separated)
PADDOCK_DEVICE--device
PADDOCK_KERNEL_PACK--kernel-pack
PADDOCK_MAX_CTX--max-ctx
PADDOCK_MAX_BATCH--max-batch
PADDOCK_MMPROJ--mmproj
PADDOCK_MTP--mtp
PADDOCK_MAX_OUTPUT_TOKENS--max-output-tokens
PADDOCK_API_KEY--api-key
PADDOCK_PDF_MAX_PAGES- (pages rendered per PDF, default 20; extra pages are dropped and the response notes it)
PADDOCK_PDF_PAGE_LONG_EDGE- (long-edge pixels per rendered PDF page, default 1568)

TOML keys use the flag names with underscores (host, model_dirs, kernel_pack, max_ctx, ...). The one exception: the --max-output-tokens flag is the max_tokens TOML key.

Example paddock.toml

host = "127.0.0.1"
port = 11540
model = "qwen3.6-27b"
model_dirs = ["~/paddock/models"]
device = "cuda"
kernel_pack = "pd-cuda-sm86.dll"
max_ctx = 16384
max_batch = 16

HTTPS

The manager serves HTTPS from the first time it starts, with no configuration and nothing to switch on. A certificate authority for the machine is generated on first run and signs a leaf covering every name and address the machine answers to; both live under <data>/tls/ and renew without being asked. A security property that has to be enabled is one that is usually off.

One port carries both schemes. The listener reads the first byte of each connection: a TLS handshake record gets TLS, anything else gets plain HTTP. So http://127.0.0.1:11500 still works for the CLI, health checks and scripts, while a browser on the network gets an encrypted origin. A network client that connected with plain HTTP is redirected instead of being handed the raw bytes of a TLS response.

If a certificate cannot be established, for instance because the key file cannot be written, that is fatal to HTTPS and not to the manager: it logs the reason and serves plain HTTP. A machine that cannot write a key is still one that should come up.

Why It Is Not Optional

Browsers withhold whole APIs from an origin that is not a secure context, and https or a localhost address are the only ways to be one. A LAN address like http://10.10.0.189:11500 is neither, and the affected APIs are not degraded there, they are absent.

Three of them matter to the Studio. navigator.clipboard and crypto.randomUUID could be worked around, and were. The microphone could not: no insecure API hands you an audio input, so dictation, live transcription and speech comparison did not exist when the Studio was opened from another machine. The only real fix was to stop serving it over plain HTTP to anyone not sitting at the machine.

Encryption was overdue on its own terms as well. A non-loopback bind already generated an API key, and then sent that key, along with every prompt, document and answer, in clear text across the network.

Trusting The Certificate

The root certificate is downloadable from the server at /tls/root.crt, with its fingerprint and the names it covers at /tls/info. Both sit outside /api so the trust page renders before the key gate does, and neither is a secret: every connecting client already receives the certificate they describe.

Installing the root on a client device stops the browser warnings for good. Not installing it also works: the warning page is unpleasant, but clicking through gives you a genuine secure context, so the microphone comes back either way.

The reason it is a CA and not a bare self-signed certificate is that a bare one can only ever be clicked through, and reissuing it would invalidate any trust already granted. A stable root can be installed once per device, and renewing the leaf or adding an address does not disturb it.

Authentication

A model server binds all interfaces by default, so other machines can reach it, and a key is what stands between them and your models. Bearer auth covers the /v1 and /api routes, and the decision is made per caller rather than per bind:

CallerKey required
LoopbackNever. Local tools and the manager work without one.
Anything elseWhenever the server holds a key, which by default it does.

With no --api-key given, a key is generated at startup and printed in the banner. So a fresh server is reachable from the network but only by someone holding that key, while everything on the machine keeps working untouched. Supplying a key makes that one required instead.

A caller whose address cannot be determined counts as not loopback, so it needs the key. When in doubt, ask for it.

--no-auth removes the requirement entirely, for deployments where a firewall or reverse proxy already carries it. The server logs a loud warning.

Clients send the key as a standard Bearer header:

curl http://192.168.1.10:11540/v1/models \
  -H "Authorization: Bearer <your-key>"

Other Environment Variables

VariableDescription
RUST_LOGLog filter. Defaults to info,paddock=debug when unset.
NO_COLORDisables ANSI color in the startup banner (also disabled automatically when stdout is not a terminal).
PADDOCK_BS_CALIBEncoder-model load calibration: off skips it, force ignores the cached result and re-measures. See Supported Models.