Skip to content

JoobQBackground jobs and durable workflows, in nine languages

JoobQ Core queues and jobs, JoobQ Pro workflows, and the JoobQUI console that runs them on your own Redis and PostgreSQL.

JoobQ

Start here

Pick your track ​

Start here

Developers

Write jobs and workflows in the language you already use.

Read the guide →

Start here

Infra and platform teams

Run it on your own PostgreSQL and Redis, and keep it healthy.

Read the guide →

Start here

Tech directors and managers

What JoobQ is, what it costs to run, and who controls what.

Read the guide →

One job, nine languages

The same job in every language ​

A job is a named handler on a queue, defined once with its retries; a returned value completes it, an exception fails it. Pick a language: the choice holds on every page of this site.

crystal
struct ConfJob
  include JoobQ::Job

  @queue = QUEUE
  @retries = 3
  @max_retries = 3
  @expires = 1.day.from_now.to_unix_ms
python
@job("conf.context", queue=QUEUE)
def context_job(payload, ctx):
    report(ctx, "context", payload, app_marker=ctx.app.marker, tenant=ctx.context.get("tenant_id"), x=payload.get("x"))
ts
defineJob<App>({ name: 'conf.context', queue: QUEUE, perform: async (p, ctx) =>
  report(ctx, 'context', p, { app_marker: ctx.app.marker, tenant: ctx.context.tenant_id, x: p.x }) }),
ruby
class ContextJob
  include JoobQ::Job
  job_name "conf.context"
  queue QUEUE
  def self.perform(p, ctx) = report(ctx, "context", p, app_marker: ctx.app.marker, tenant: ctx.context["tenant_id"], x: p["x"])
end
go
joobq.DefineJob[*App]("conf.context", opts, func(p map[string]any, ec *EC) (any, error) {
	return report(ec, "context", p, map[string]any{"app_marker": ec.App.Marker, "tenant": ec.Meta["tenant_id"], "x": p["x"]})
}),
rust
JobDef::new("conf.context", queue.clone(), |p: Value, ctx: &Ctx| {
    report(ctx, "context", &p, json!({"app_marker": ctx.app.marker, "tenant": ctx.context.get("tenant_id"), "x": p["x"]}))
}),
java
JobDefinition.<App>of("conf.context", QUEUE, 3, (p, ctx) ->
    report(ctx, "context", p, map("app_marker", ctx.app().marker, "tenant", ctx.context().get("tenant_id"), "x", m(p).get("x")))),
php
final class ContextJob extends ConfJob
{
    public static function jobName(): string { return 'conf.context'; }
    public static function perform(array $p, ExecutionContext $ctx): mixed
    {
        return report($ctx, 'context', $p, ['app_marker' => $ctx->app->marker, 'tenant' => $ctx->context['tenant_id'] ?? null, 'x' => $p['x'] ?? null]);
    }
}
csharp
JobDefinition<App>.Define("conf.context", Queue, 3, (p, ctx) =>
    Report(ctx, "context", p, new() { ["app_marker"] = ctx.App.Marker, ["tenant"] = ctx.Context.GetValueOrDefault("tenant_id")?.GetValue<string>(), ["x"] = Num(p?["x"]) })),

Tested against a real server in every language shown.

Then a worker connects to the server's SDK gateway with the origin and the SDK token and long-polls for tasks: SDKs: install and connect.

The console

Operate it from one place ​

The JoobQUI console's Overview page: active workflows, jobs per second, failed jobs, worker nodes and p99 latency, with throughput and latency charts and the queues below.The JoobQUI console's Overview page: active workflows, jobs per second, failed jobs, worker nodes and p99 latency, with throughput and latency charts and the queues below.
The console's Overview page (mock data). Queues, jobs, dead letters, workers, schedules, workflows, access and audit each have a page of their own.

JoobQ Core is MIT licensed. JoobQ Pro needs a licence key.