Workhorse
Reference

Limitations

What Workhorse does not do today — with the workaround for each boundary that has one.

Choosing infrastructure means knowing what it will not do before it is load-bearing. Workhorse fits PostgreSQL-backed applications that accept at-least-once handlers and exact schema compatibility. This page lists every boundary honestly, with the workaround where one exists.

Workhorse is a public beta. It is usable for evaluation and early production adoption, but any minor release may break compatibility, including the schema. There is no upgrade path between 0.x releases; ordered migrations begin at 1.0.0.

Workflow orchestration lives in handlers

Workhorse supports job prerequisites, fan-in policies, child jobs, external signals, and human decisions. It does not provide a separate workflow definition language or graph deployment model.

Workaround: compose those durable boundaries in ordinary handler code. If a workflow must be defined and deployed independently of application code, use a workflow system on top or instead.

No cross-queue concurrency policies

Concurrency and rate-limit policies are scoped to one queue. You cannot express "at most 10 active jobs across these three queues".

Workaround: route work that shares a budget onto one queue, and use concurrencyKey for per-tenant budgets within it.

Cancellation is cooperative, never forced

JavaScript cannot be preempted safely, so Workhorse never kills a running handler. Cancellation, deadlines, and execution timeouts abort the handler's AbortSignal; the code runs until it observes that signal or loses its lease. Process shutdown likewise drains handlers rather than preempting them.

Workaround: pass context.signal into every cancelable operation and check it between units of work. If work truly requires forced termination, isolate it in a process you can kill and make its effects recoverable.

External effects are at least once

PostgreSQL cannot commit an HTTP call, email, or payment in the same transaction as job completion. A worker can perform the effect and die before recording success, so the effect can happen again. Enqueue idempotency keys deduplicate job identities; they cannot make one accepted handler execute exactly once. Redrive is another at-least-once execution.

Workaround: this is the designed model, not an accident. Wrap each effect in a named checkpoint so completed stages never rerun, and give the provider an idempotency key — the job ID or a domain ID — to close the final gap between effect and checkpoint commit.

Schema and terminal tooling ship in TypeScript

Python and Go ship transactional queue clients, complete worker runtimes, and Admin operator clients over the shared SQL protocol. Schema installation and migration (migrateSchema) and the workhorse admin CLI and TUI ship only in the TypeScript package, so a polyglot deployment keeps that package available for those operations.

No public HTTP ingress

Nothing accepts jobs over HTTP. Producers need a database connection and the SDK.

Workaround: enqueue from your own API routes — which is also where transactional enqueue with your business writes lives.

Retired pre-release schemas have no upgrade source

migrateSchema applies ordered, forward-only migrations from the supported baseline. Schemas at versions retired before that baseline have no migration history, so operators must reset those development schemas instead of upgrading them.

Workaround: keep schema changes in the deployment path and call migrateSchema — or workhorse schema migrate — before starting a new runtime. The changelog records each release's required schema version.

Dashboard authentication has one built-in role

The standalone dashboard provides one administrator login with expiring sessions, logout, credential rotation, and login throttling. It does not provide multiple users, roles, SSO, or tenant isolation.

Workaround: mount the dashboard behind your application's existing authentication boundary and return its verified operator from authorize.

Next


Exact delivery guarantees, timing bounds, and unsupported protocol behavior: architecture reference.