Deploying workers
Register your jobs with runnel deploy, run workers in containers, on Kubernetes or as functions, and shut them down without losing work.
Deploying Runnel has two halves. runnel deploy tells Runnel which jobs, workflows and schedules exist in an environment. Your workers, which you run wherever you run the rest of your code, do the work. They connect out to Runnel over HTTPS, so they need no open ports and no public address.
runnel deploy
Run runnel deploy from your project, usually as a step in CI after your tests pass. It reads runnel.config.ts, loads your worker file and registers every definition it finds as a new version.
$ RUNNEL_API_KEY=rk_live_… runnel deploy --env production
Loading src/worker.ts
jobs send-invoice, sync-supplier, usage-report
workflows onboard-customer
schedules nightly-usage (0 2 * * * Europe/London), supplier-sync (*/15 * * * *)
Deployed v42 to production (3 jobs, 1 workflow, 2 schedules)Versions are numbered per environment. Each run records the version it was enqueued under, which you can see in the dashboard and in the Runs API. Deploying does not interrupt runs in progress.
Note Deploy definitions and roll out workers together. A run for a job that no worker knows about waits in the queue, and the dashboard flags it as “no worker”, until one that has the job connects.
Running workers
A worker is a long-running process that calls runnel.worker(...).start(). It needs two environment variables: RUNNEL_API_KEY for the environment, and RUNNEL_ENV. Run as many copies as you need; Runnel hands each run to exactly one of them.
FROM node:22-slim
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile --prod
COPY dist ./dist
ENV RUNNEL_ENV=production
STOPSIGNAL SIGTERM
CMD ["node", "dist/worker.js"]The concurrency option on the worker sets how many runs one process handles at once. For I/O-bound jobs (HTTP calls, email, database writes) 50 is a reasonable start. For CPU-bound jobs, set it near the number of cores and scale with more processes instead.
Kubernetes
Workers are an ordinary Deployment. Give them a grace period a little longer than the worker’s own (below) and scale on queue depth rather than CPU: Runnel exposes runnel_queue_depth per job on the worker’s metrics endpoint (:9464/metrics when metrics: true).
apiVersion: apps/v1
kind: Deployment
metadata: { name: runnel-worker }
spec:
replicas: 3
selector: { matchLabels: { app: runnel-worker } }
template:
metadata: { labels: { app: runnel-worker } }
spec:
terminationGracePeriodSeconds: 45
containers:
- name: worker
image: registry.example/acme/worker:1.18.0
envFrom: [{ secretRef: { name: runnel } }]Serverless functions
If you would rather not run long-lived processes, Runnel can push runs to an HTTPS endpoint instead. Export a handler and register its URL with runnel deploy --push-url:
import { runnel } from '@runnel/sdk';
import { sendInvoice } from '../src/jobs/send-invoice';
export const POST = runnel.handler({ jobs: [sendInvoice] });Each request carries one attempt and is signed with Runnel-Signature, which the handler checks. Mind your platform’s time limit: a job whose timeout is longer than the function’s maximum duration will be cut off. Workflows suit this model well, because each step can finish inside one short request.
Graceful shutdown
When a worker receives SIGTERM, it stops taking new runs, lets in-flight runs finish and then exits. It waits up to 30 seconds by default; change it with shutdownGracePeriod.
runnel.worker({ jobs, concurrency: 50, shutdownGracePeriod: '40s' }).start();Runs still going at the end of the grace period are aborted through ctx.signal, and their attempt is released back to the queue without counting as a failure. If a worker disappears without warning (a killed container, a lost node), Runnel notices the missing heartbeat within 30 seconds and requeues its runs.
Tip Set your platform’s grace period about five seconds above the worker’s, so the worker exits on its own terms before it is killed.
Environments and keys
Create a key per environment with runnel keys create --env staging and keep it in your secret store. Keys starting with rk_dev_ only work against development; rk_live_ keys are for everything else. See the CLI reference for the full list of commands.