Runnel 2.8: replay any workflow from a failed step.Read the release

Skip to content
Runnel
Documentation

    Docs menu

    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.

    Dockerfile
    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).

    k8s/worker.yaml
    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:

    api/runnel.ts
    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.

    Edit this page