Worker

Configuration

Worker configuration comes from three sources, merged in order (later wins):

  1. Built-in defaults compiled into the daemon.
  2. Config file at ~/.pendra/config.yaml (Unix) or %ProgramData%\Pendra\config.yaml (Windows).
  3. Environment variables.

pendra setup writes the config file interactively; pendra config set <key> <value> mutates it directly. Env vars are re-read on every CLI invocation and at daemon startup.

Example config file

~/.pendra/config.yaml
# ~/.pendra/config.yaml
app_server_ws_url: wss://api.pendra.ai
gpu_worker_private_key: <base64-ed25519-private-key>
worker_id: wrk-a1b2c3d4
worker_name: gpu-01
models_dir: ~/.pendra/models
# optional — private inference (set by 'pendra keygen --save'):
workload_private_key_file: ~/.pendra/workload_key

The file holds the Ed25519 private key in plaintext, so the daemon enforces mode 0600 on Unix and warns at startup if it's wider. On Windows the config directory and key file are restricted to SYSTEM, Administrators, and the account Pendra runs as, so other local accounts can't read the key.

Settings reference

Env varConfig keyDefaultPurpose
APP_SERVER_WS_URLapp_server_ws_urlwss://api.pendra.aiBase URL the daemon dials; /ws/gpu is appended automatically.
GPU_WORKER_PRIVATE_KEYgpu_worker_private_keyBase64 Ed25519 private key from the console. The daemon refuses to start without one.
PENDRA_WORKLOAD_PRIVATE_KEY_FILEworkload_private_key_filePath to the private-inference workload key file. pendra keygen --save writes a PEM X25519 key beside config.yaml and sets this; you can equally point it at a PEM X25519 key you generated yourself (e.g. openssl genpkey -algorithm X25519). Leave it unset and drop a key file at the default location (~/.pendra/workload_key) and the worker picks it up automatically. Off unless a key is configured or found there — see private inference.
PENDRA_WORKLOAD_PRIVATE_KEY_FILE_PREVIOUSworkload_private_key_file_previousPath to the previous workload key during a key rotation (same base64 or PEM encodings as the primary). Set automatically when pendra keygen --save rotates an existing key, so the worker keeps serving clients still pinned to the old fingerprint until they re-pin.
WORKER_IDworker_idauto wrk-<hex>Stable ID across restarts. Persisted on first run.
WORKER_NAMEworker_namehostnameFriendly name shown in the console. You don't need to set it — see Naming a worker. The environment variable overrides a console rename on every restart; the config-file key is overridden by one.
MODELSmodels(serve all)Filter to a subset of discovered models. JSON array of IDs or {id} objects.
PENDRA_MODELS_DIRmodels_dir~/.pendra/models (Unix), %ProgramData%\Pendra\models (Windows)Directory the worker serves GGUFs from. pendra models install downloads into this directory; pendra models dir prints the resolved path.
PENDRA_KV_CACHE_DIR~/.pendra/kvcache (Unix), %ProgramData%\Pendra\kvcache (Windows)Where the KV cache stores its saved conversations when you set it to Memory + NVMe. Point it at a fast local SSD — that's where the speed-up comes from. Saved copies can run to several gigabytes each; the worker keeps the total under the size cap (250 GB by default, adjustable per worker in the console), dropping the least recently used first. Run pendra config on the worker to see the directory it resolved.
PENDRA_DISABLE_LLAMACPPfalse (on)Set 1/true/yes to turn off Pendra's in-process chat and embeddings.
PENDRA_DISABLE_WHISPERCPPfalse (on)Set 1/true/yes to turn off Pendra's in-process audio transcription. Transcription accepts wav/mp3/flac and runs on Linux, macOS, and Windows workers.
PENDRA_DISABLE_WHISPERCPP_VADfalse (on)When transcribing a file, Pendra automatically skips silent and non-speech passages, which keeps quiet gaps from producing repeated words. Set 1/true/yes to turn that off and transcribe the audio unaltered.
PENDRA_WHISPER_MAX_PARALLEL4How many audio transcriptions a worker runs at the same time, from 1 to 16. Concurrent transcription requests now run in parallel up to this limit (and up to Requests accepted at once) instead of queuing one behind another, so a burst of uploads finishes sooner. Extra parallel capacity is only used when concurrent requests actually arrive. Set it to 1 to transcribe strictly one at a time; raise it for high-volume transcription.
PENDRA_DISABLE_STABLEDIFFUSIONfalse (on)Set 1/true/yes to turn off Pendra's in-process image generation. Serves /v1/images/generations from a diffusion .gguf in your models directory.
PENDRA_DISABLE_AUDIOCPPfalse (on)Set 1/true/yes to turn off Pendra's in-process text-to-speech. Serves /v1/audio/speech from a speech model in your models directory.
PENDRA_DISABLE_NATIVE_TOOLSfalse (on)When you send tools with a request, Pendra describes them to the model in the model's own built-in tool-calling format wherever it has one — the format that model was trained on — which makes it pick tools more reliably. Set 1/true/yes to turn that off and use Pendra's generic tool instructions for every model instead. Only worth setting if a model starts calling tools less reliably after a worker upgrade.
PENDRA_ALLOW_METAL_BF16false (refuse)Apple Silicon Macs only. A few models whose weights use the bf16 number format are declined on a Mac worker by default, because they can crash it. Set 1/true/yes to run one anyway once you've confirmed it works on your Mac. No effect on other platforms.
PENDRA_BLOCK_METAL_BF16_PROJECTORfalse (allowed)Apple Silicon Macs only. Vision models run on Mac workers by default. If a vision model's image analysis fails to start on an older Mac, set 1/true/yes to serve those models as text-only (image input off). This only affects image input — it won't make a model that can't run at all (e.g. a too-large or unsupported one) start running. No effect on other platforms.
INSECURE_TLSinsecure_tlsfalseSkip TLS cert verification for the WebSocket. Local dev only.
PENDRA_LOG_MAX_FILES5How many log files to keep in ~/.pendra/logs/. The worker starts a fresh log on each restart and keeps this many (the live file plus older sessions), so the log from before a crash or restart is still there to read. Minimum 2.
PENDRA_LOG_MAX_SIZE_MB20Size in MB at which the current log file rolls over to a new one mid-session. The total kept is still bounded by PENDRA_LOG_MAX_FILES. Minimum 1.

Naming a worker

Installing a worker never asks you for a name. It starts out named after the machine it's running on — its hostname — so a fresh worker shows up in the console already recognisable.

To change it, open Workers → your worker in the console and click the pencil next to the name. Type the new name and save. The worker has to be connected: the console sends the new name to it, and it takes effect within a couple of seconds — no restart, and the name sticks across restarts. You need to be an owner or operator, and names are up to 64 characters.

If you'd rather set the name where you deploy the worker — a Docker -e WORKER_NAME=gpu-01, a Kubernetes manifest, a systemd unit — you still can, and it's the better fit for containers, whose hostname is an unhelpful random string. Just note that the WORKER_NAME environment variable wins every time the worker restarts, so a rename you make in the console will be replaced by it on the next restart. Pick one place to name a given worker and stay there.

The worker_name key in config.yaml works the other way round: a rename you make in the console takes precedence over it. To set the name on the machine instead, run pendra config set worker_name gpu-01 — that clears the console name for you — then restart the worker.

Per-worker settings in the console

A few worker-wide tuning options live in the console rather than in config.yaml or an env var. Open Workers → your worker → Settings (you must be an owner or operator) — changes apply to the running worker within a few seconds, no restart needed.

SettingDefaultPurpose
KV cacheOffKeep the active model's conversation warm between requests so long, multi-turn chats reply faster. Memory holds it in GPU memory; Memory + NVMe also saves it to the worker's disk, so a shared prompt prefix stays fast across model swaps, idle unloads and restarts. The disk tier has a size cap — 250 GB by default, adjustable here when Memory + NVMe is selected — and stores its files under PENDRA_KV_CACHE_DIR (above). See KV cache.
Speculative decoding cap2How many tokens the draft proposes per step when a model runs speculative decoding (range 16). Turning speculative decoding on or off is a separate per-model setting (Models & context); this tunes the depth when it's on. It has no effect on a worker with continuous batching switched on — those workers serve requests without a draft step, so speculative decoding isn't used at all. See Speculative decoding.
Allow remote image URLsOffLet vision requests reference http(s) image URLs, which the worker fetches for you (private/loopback hosts are always blocked). Off by default — pass images inline as base64 data: URIs.
Web toolsOffLet tool-capable models fetch live web pages and run web searches during a chat, so answers can use up-to-date information (private/loopback hosts are always blocked). Off by default — turning it on adds extra work per request. When it's on, the chat response also lists the pages fetched and searches run under a pendra.web_tool_steps field, so you can see what the model looked at.
Task timeout30mWall-clock limit for a single non-streaming request (chat, embeddings, rerank, image, transcription), from 1 to 120 minutes. Raise it for very large or slow models; lower it to fail faster. Streaming chat is unaffected.
Response format grammarOnWhen you request JSON output (response_format json_object or json_schema), the worker constrains the model as it generates so the reply is JSON matching your schema. On by default. Turn it off only as an escape hatch if a particular model on this worker fails to generate with it enabled.
Requests accepted at once1How many requests Pendra sends this worker before routing to another one (range 11024). Raising it keeps traffic from being turned away; on its own it does not make anything faster — what the worker does with those requests depends on the next two settings.
Run requests togetherOffDecode several requests in one shared pass instead of one after another, so the same GPU serves far more work. Off by default; turn it on for high-volume parallel workloads.
Requests running at onceAll acceptedOf the requests the worker accepts, how many decode at the same time. The rest wait on the worker for a free slot. Only applies while Run requests together is on. Lower it to keep each reply fast; raise it for maximum total throughput. A change takes effect the next time the model is idle.
Auto-unload idle modelsOn · 15 minUnload any model that isn't Always-on after this many minutes with no requests (range 101440), freeing its GPU memory until the next request reloads it. On by default so idle models don't sit on memory; turn it off to keep every model resident once loaded. A model whose residency is set to Custom uses its own window instead of this one.

Accepted, running, waiting

These three settings are easier to reason about as one pipeline. A request Pendra sends here is accepted; a request the GPU is currently generating is running; anything in between is waiting on this worker for a free slot.

Generating a reply means running the model once per token. A single pass that produces one token for one request costs almost the same as a pass that produces one token for eight, because the GPU spends most of it waiting on memory rather than calculating. That is why Run requests together is worth turning on: eight requests served together finish in barely more time than one served alone, where eight served one after another take roughly eight times as long.

With Run requests together off, this worker generates one reply at a time no matter how many it accepts — the rest simply queue here. So raising Requests accepted at once on its own does not add speed; it only decides where requests go.

Splitting the two matters most when you run a single worker. Say sixteen requests arrive at once on one GPU:

  • Accept 8, run 8 — the ninth request has nowhere to go and is rejected.
  • Accept 16, run 16 — nobody is rejected, but one GPU's output is split sixteen ways, so every reply is slow.
  • Accept 16, run 8 — nobody is rejected, eight stream at full speed, and the rest start the moment a slot frees.

Going from one to around eight running at once costs each individual reply very little. Pushing it much higher keeps raising total throughput, but every reply gets noticeably slower — so a bulk or batch job wants it as high as it goes, while anything a person is waiting on wants it moderate. It is not a memory setting: lowering it does not free GPU memory, which is reserved from the model's context window instead.

A few model families always generate one reply at a time regardless of these settings, because of how they store conversation state. The console says so on the model itself.

CLI commands

CommandPurpose
pendra / pendra runStart the worker daemon.
pendra statusConnection and model info for the running worker.
pendra restart-backendRestart just the inference engine (a fresh process starts on the next request) without stopping the worker. Use it to recover a stalled GPU that has fallen back to running on CPU — the console flags that with a "Running on CPU" badge. Pendra also tries to recover on its own; the command forces it.
pendra configPrint resolved config and the file path it loaded from.
pendra config set <key> <value>Mutate the config file.
pendra config repair-permsRe-tightens the config file's permissions: chmod 600 on Unix, or re-applies the restrictive ACL (SYSTEM, Administrators, and the account Pendra runs as) on Windows. Useful if the worker warns that the config file has loose permissions — the daemon self-heals on startup, but this command forces the repair immediately. Run as an administrator on Windows.
pendra setupInteractive setup wizard.
pendra modelsList the models installed on the worker.
pendra models install <model>Install a catalogue model onto the worker.
pendra logsTail the worker's log buffer (-f follows). The OS-supervised service is managed with systemctl / launchctl / Services.msc.
pendra doctorDiagnostics — checks the inference runtime, config, and the live worker connection. On a Linux service install, re-runs itself with sudo when it needs to read the service's config.

Related