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

Skip to content
Runnel
Documentation

    Docs menu

    Introduction

    What Runnel is, what it handles for you and how the pieces fit together: jobs, workflows, schedules, workers and the dashboard.

    Runnel runs background work for TypeScript, Python and Go applications. You write a job as an ordinary function in your own codebase. Runnel queues it, runs it on your workers, retries it when it fails, runs it on a schedule if you ask it to, and keeps a record of every run with its logs, inputs and outputs.

    It replaces the parts most teams build by hand around a queue: the retry loop, the cron box nobody owns, the table of half-finished jobs, the script that re-runs failures on Monday morning.

    What you get

    • Jobs with retries, timeouts, delays, idempotency keys and concurrency limits, set next to the code they apply to.
    • Workflows made of named steps. A finished step is never run twice, so a payment captured in step two is not captured again when step four fails.
    • Schedules on cron expressions, in a time zone of your choice, which you can pause from the dashboard or the CLI.
    • A dashboard that shows every run: its payload, each attempt, each step, the logs and the error that ended it. You can retry or cancel a run from there.
    • A CLI and a REST API for everything the dashboard does, so runs can be inspected and retried from scripts and CI.
    • Webhooks when a run succeeds, fails or is about to be retried.

    How it fits together

    Runnel has two halves. Your code runs in workers: ordinary processes you start in your containers, on your servers or in a serverless function. Runnel Cloud holds the queue, the state of every run, the schedules and the logs.

    Workers connect out to Runnel over HTTPS and pull work. Runnel never calls into your network, so there is nothing to open in your firewall, and your secrets, database and third-party credentials stay where they are.

    src/jobs/send-invoice.ts
    import { runnel } from '@runnel/sdk';
    
    export const sendInvoice = runnel.job({
      name: 'send-invoice',
      retries: { attempts: 5, backoff: 'exponential' },
      timeout: '2m',
      run: async (payload: { invoiceId: string }, ctx) => {
        const invoice = await billing.invoices.get(payload.invoiceId);
        await mailer.send(invoice.customerEmail, renderInvoice(invoice));
        ctx.log.info('invoice sent', { invoiceId: invoice.id });
      },
    });

    Anywhere in your application, enqueue it:

    await sendInvoice.enqueue({ invoiceId: 'inv_8812' });

    The call returns as soon as the run is stored, with a run id such as run_01JB7X2K9Q4MZ8. A worker picks it up within a few milliseconds.

    Note If you would rather run the whole thing yourself, Runnel Server is the same backend as Runnel Cloud, open source under Apache 2.0. See Self-hosting.

    When to use Runnel

    Runnel suits work that should happen outside a request, must not be lost and may need more than one attempt: sending email and invoices, calling slow or unreliable APIs, processing uploads, syncing with another system, nightly reports, multi-step processes such as onboarding or order fulfilment.

    It is not a stream processor or a message bus. If you need millions of small events a second fanned out to many consumers, use a log such as Kafka and call Runnel for the work those events trigger.

    Where to go next

    If you want toRead
    Run your first job in five minutesQuickstart
    Add the SDK to an existing projectInstallation
    Understand runs, attempts, steps and environmentsCore concepts
    Build a multi-step processWorkflows
    Call Runnel from another languageRuns API

    Edit this page