all posts

The best Inngest alternatives in 2026

Ajay Kumar··13 min read

The search for an Inngest alternative almost never starts with a feature gap. It starts with a bill that grew faster than the traffic did, or a compliance questionnaire asking where step outputs are stored, or an engineer who spent a Thursday discovering that the function which worked perfectly in the dev server behaves differently when a real cloud is invoking it over HTTP. By the time somebody types the query, the actual question underneath is usually narrower and more interesting: was a step DSL ever the right shape for this job, or did we adopt one because our compute could not stay alive for more than a few seconds?

I'm Ajay, I build PandaStack. Disclosure first, because it changes how you should read the rest: we do not sell a workflow engine, a step DSL, or a hosted job service, and we are not building one. We run Firecracker microVMs, apps, functions and managed Postgres — so we host the worker process and the database underneath somebody else's queue. That means I have no engine to sell you, and it also means my recommendation in the last section is genuinely one option among many rather than the point of the article. Inngest is a good product. Several of the alternatives below are also good products. The useful work here is matching the shape to the problem.

What Inngest actually is, stated accurately

Inngest is event-driven durable functions. You send events; functions declare which events trigger them; the platform runs those functions in a way that survives crashes, deploys and long waits. The mechanism that makes it different from every other entry on this list is the invocation model: instead of your workers polling a broker, Inngest calls an HTTP endpoint you expose — typically something like a route at /api/inngest — once per step, and memoises the result of each step it completes.

  • A function body is ordinary application code. The durable boundary is a step wrapper — step.run for work, step.sleep and step.sleepUntil for waits, step.waitForEvent for a pause until something happens, step.invoke to call another function. Anything inside a wrapper runs at most once and its result is recorded.
  • Because each step returns quickly and the platform re-invokes your endpoint for the next one, a multi-hour workflow fits inside a runtime with a short request timeout. That is the whole reason this model exists, and it is a genuinely clever answer to serverless constraints.
  • Flow control is declarative configuration rather than code: concurrency keys, throttling, rate limiting, debounce, batching, prioritisation, singleton-style controls. This is the part teams miss most when they leave, and I will come back to it.
  • SDKs cover TypeScript, Python and Go, and the serve handler adapts to a long list of frameworks and runtimes, which is why it shows up so often in Next.js codebases.
  • Local development runs against a dev server you start on your machine, which discovers your functions and replays events. It is one of the better local stories in this category and I will not pretend otherwise.

Read that as an architecture, not a feature list. Every one of those choices buys something specific and costs something specific, and the five reasons teams go shopping map onto them almost one to one.

The five reasons teams go looking

Cost shape at step volume. When the unit of billing is related to steps and runs rather than CPU-seconds, the incentive gradient points at coarse steps — which is the opposite of what the programming model encourages, because fine-grained steps are what give you good recovery granularity. Teams discover this when a loop over a few thousand records turns into a step per record and the invoice does something surprising. I am deliberately not printing numbers here; check their current step-and-run pricing against a real workflow of yours, with the step count you would actually write, before and after you optimise it.

Wanting to self-host. This comes from procurement more often than from engineering. Step outputs are memoised by the platform, which means intermediate data — sometimes including data you would rather not export — lives in someone else's system for the life of the run. Inngest has published self-hosting material and parts of the stack are open source; if this is your constraint, read the current licence and what a self-hosted build actually includes, because 'open source dev tooling plus a proprietary control plane' and 'you can run the whole thing' are very different answers and the distinction moves over time.

Wanting a plain long-running worker. This is the big one, and it is the reason I think most of these evaluations reach the wrong conclusion. A lot of teams adopted a step DSL because their compute could not run for more than a few seconds, not because their problem was step-shaped. If you can run an ordinary process for an hour, the six-step function collapses into a function that calls six other functions, with a retry decorator and an idempotency key. That is not a downgrade. It is the code you would have written first.

Lock-in of the step API. Business logic written as memoised closures inside a vendor's wrapper is genuinely awkward to unwind — not impossible, but the seams are in the wrong places, because step boundaries were chosen for recovery and billing rather than for domain structure. This is the same complaint people make about Step Functions ASL and about Temporal's workflow determinism rules; it is a category property, not an Inngest failing.

Local and preview-environment friction. The dev server is good. The friction shows up one layer out: in production your steps are invoked by a control plane reaching into your deployment, so every environment that needs to run functions must be reachable and registered, and branch or preview environments need a mapping story. Teams on a single production deploy rarely feel this. Teams with a preview environment per pull request feel it every week.

Before you evaluate anything, count your steps per run and your runs per month, and separately count how many of your functions genuinely need a wait longer than one request. In my experience most codebases have two or three functions that truly need durable waits and forty that are just background jobs wearing the same costume. Those forty do not need any of the products in this post.

The same job, written two ways

Here is a realistic onboarding flow — charge, provision, wait three days, send a nudge if they have not activated — in the step-function style, and then as a plain durable worker. Read them side by side, because the difference is the entire decision.

// A. Step-DSL style. The platform calls this endpoint once per step and
// memoises each result, so the process can die between any two steps.
export default inngest.createFunction(
  { id: "onboard", concurrency: { key: "event.data.orgId", limit: 1 } },
  { event: "org/created" },
  async ({ event, step }) => {
    // Each step.run body runs at most once. Its return value is serialised
    // and stored by the platform, then replayed on the next invocation.
    const customer = await step.run("billing", () =>
      billing.createCustomer(event.data.orgId));

    await step.run("provision", () =>
      provisionWorkspace(event.data.orgId, customer.id));

    // The wait is free: no process is held open across it. This is the
    // single feature that justifies the whole model.
    await step.sleep("settle", "3d");

    const active = await step.run("check", () =>
      hasActivated(event.data.orgId));

    if (!active) {
      await step.run("nudge", () => email.send(event.data.email, "nudge"));
    }
  },
);
// B. Plain durable worker. Same guarantees, different location for the
// state: a row you own, in a database you already run. No step DSL, no
// control plane invoking you, no memoised payloads leaving your network.
//
// The trade is explicit: YOU write the idempotency. There is no replay,
// so a crash restarts the handler, and every side effect must tolerate
// being attempted twice.

async function onboard(job: { orgId: string; email: string }) {
  // Idempotency key instead of memoisation. The billing provider dedupes;
  // that is what makes the retry safe, not the queue.
  const customer = await billing.createCustomer(job.orgId, {
    idempotencyKey: `cust:${job.orgId}`,
  });

  // Naturally idempotent by construction: upsert, not insert.
  await provisionWorkspace(job.orgId, customer.id);

  // The long wait becomes a second job, scheduled three days out. This is
  // the honest translation of step.sleep -- a row with a run_at, not a
  // suspended function. It is also the part people forget to write.
  await queue.add("onboard:nudge", job, { delay: 3 * 24 * 3600 * 1000 });
}

async function onboardNudge(job: { orgId: string; email: string }) {
  if (await hasActivated(job.orgId)) return;
  await email.send(job.email, "nudge", { dedupeKey: `nudge:${job.orgId}` });
}

// Concurrency-per-org, the thing the step DSL gave you as one config line,
// costs you a line here too -- but only because BullMQ happens to have it.
// Check that your queue library does before you assume this is free.
new Worker("onboard", handler, { concurrency: 20, connection });

Version B is longer in one place and shorter everywhere else. It is longer where idempotency is now your problem, which the step model was hiding. It is shorter in the sense that there is no vendor between the two halves, no per-step serialisation of your customer data, and no HTTP hop per step. Which one is better depends entirely on whether you can keep a process alive, and on how many workflows genuinely need a three-day pause rather than a delayed job.

Category one: durable execution engines

These are the products that share Inngest's core promise — a workflow survives the process running it — and differ on where the state lives and who invokes whom. If you want depth on this category specifically, I wrote a longer comparison of durable execution platforms; here I will keep each to what matters if you are coming from Inngest.

Temporal

The reference implementation and the most complete primitive set anywhere on this list: signals, queries, child workflows, continue-as-new, schedules, search attributes. Coming from Inngest, the two adjustments are architectural. First, the invocation direction flips — your workers poll task queues rather than being HTTP-invoked, so you are back to running long-lived processes. Second, workflow code must be deterministic and is replayed from history, which is a stricter contract than a memoised step: a Date.now() in a workflow function is a bug that only appears during recovery.

What you gain is that everything runs on your infrastructure with your secrets, and the vocabulary is rich enough that complicated orchestration has a name rather than a workaround. What you pay is operational: self-hosted Temporal is several services plus a persistence store, and usually a search store once support asks to find all workflows for one customer. Temporal Cloud takes the cluster half and leaves the workers with you, which is a clean division of labour but not a removal of ops.

Restate

The closest thing to a like-for-like swap in spirit, and the one I would look at first if you like Inngest's model but want the engine on your own machines. Restate ships as a self-contained binary with an embedded log — no separate database to provision, no search cluster — and it invokes your registered service handlers, which is the same inversion Inngest uses. Your handlers are ordinary code; the runtime journals their steps and their calls to each other.

The programming model is flatter than Temporal's: durable RPC and durable promises as first-class things, plus virtual objects that give you keyed single-writer concurrency without hand-building a lock — which is a decent structural answer to the concurrency-key feature you are giving up. It is younger than Temporal, so the ecosystem and the supply of engineers who have operated it are correspondingly smaller.

DBOS

The minimum-infrastructure answer and the one most likely to be skipped in an evaluation. DBOS is a library rather than a cluster: you add it to your application, annotate the functions you want to be durable, and it checkpoints their state into your own Postgres, in a schema next to your own tables. There is no orchestrator in the request path and no second system to page you.

The consequences are pleasant if you are leaving Inngest over data residency or dependency concerns, because durable state is transactional with your business data and never leaves your database. Local development is your app plus a Postgres. The flip side: your Postgres is now on the orchestration critical path, language coverage is narrower than Temporal's, and there is no rich flow-control layer waiting for you — throttling and fairness are yours to build.

Category two: hosted job and workflow services

Trigger.dev

The most direct competitor and usually the first alternative anyone tries, because the positioning overlaps heavily: TypeScript-first background jobs and long-running tasks with retries, schedules, concurrency controls and a good run dashboard. The architectural difference worth understanding is that Trigger.dev's current generation runs your task code on their compute rather than calling back into your deployment, which removes the reachable-endpoint requirement that causes preview-environment friction — and adds the usual consideration that your code and its dependencies now execute in their runtime.

It is open source and self-hostable, which is the reason it appears in most evaluations that start with a procurement objection. If you go that route, treat the self-hosted deployment as a real system with real components rather than a checkbox, and confirm which features are gated. Their model is closer to 'long-running task with checkpoints' than to a step-per-HTTP-call graph, so the migration is often less mechanical than it looks.

Hatchet

Postgres-backed task orchestration that grew up from the queue side rather than down from the workflow side. You get a durable task queue with DAG-shaped dependencies, retries, concurrency controls and fairness keys, plus a dashboard that treats run observability as a product feature rather than a debugging afterthought. Open source with a managed cloud, and Postgres as the store.

Coming from Inngest, the trade is the invocation model again: workers hold a long-lived connection, so this is poller-shaped. In exchange, the flow-control vocabulary — concurrency keys, fairness, rate limits — is the closest match on this list to what you would be giving up, which matters more than most migration guides admit.

The Defer-shaped middle, and a warning about it

There was a whole generation of hosted background-job products aimed at exactly this niche — write a function, add a decorator, let us run it — and Defer was one of the better-known ones. Several of them are no longer operating. I am not going to publish a list of who is alive this quarter, because the answer changes and a stale list is worse than none, but the lesson generalises: this category has a high mortality rate, because the product is thin relative to the infrastructure needed to run it and the buyers are price-sensitive.

So make it an explicit evaluation criterion. Ask how much of your code would need rewriting if the vendor disappeared with ninety days' notice, and prefer the answer where your handlers are plain functions and the vendor-specific part is a thin registration layer. That question is worth more than any feature comparison you can build.

Category three: a plain queue and a worker

This is the category I think is under-chosen, and it is the honest destination for most teams whose complaint was cost shape or lock-in rather than a missing feature. There is no durable replay here. A crash restarts your handler from the top, so every handler must be idempotent by hand. In return there is no control plane, no per-step billing, no serialisation of intermediate data anywhere outside your infrastructure, and the whole thing is inspectable with tools you already have.

  • BullMQ on Redis — the default for Node and TypeScript teams. Delayed and scheduled jobs at arbitrary timestamps, repeatable jobs, priorities, retries with backoff, rate limiting, flows for parent-child dependencies, and a dashboard your on-call can read. The closest thing to a feature-for-feature answer to Inngest's flow control, in a library. The cost is Redis: memory-bound backlogs and persistence guarantees that are real but not WAL-backed.
  • Celery — the Python equivalent, with two decades of production behind it and a broker decision (Redis or RabbitMQ) that is genuinely separate from the worker decision. Beat handles schedules. Its age shows in the configuration surface, and the visibility-timeout behaviour on Redis brokers is the single most common source of duplicate task execution.
  • River — Postgres-backed job queue for Go, using SELECT ... FOR UPDATE SKIP LOCKED under the hood, with transactional enqueue so a job and the row that caused it commit together. If you are a Go shop who wanted durable functions, look here before you look at an engine.
  • Sidekiq — the Ruby answer, still one of the best-operated pieces of software in this space, and the reason a lot of Rails teams never needed a workflow product at all.
  • pg-boss and Oban — the same SKIP LOCKED idea packaged for Node and Elixir respectively. Both give you the transactional-enqueue property, which no external broker or hosted service on this page can offer.

The transactional-enqueue point deserves emphasis because it is the one genuine advantage the database-backed options have over everything else in this post, Inngest included. When the job row is inserted in the same transaction as the data change that caused it, the classic bug where your database commits and the event publish does not becomes structurally impossible. No amount of retry logic in a hosted service can give you that, because the service is on the other side of a network call.

If you migrate from a step DSL to a plain queue, the failure you will actually hit is not throughput. It is a handler that was safe under memoisation and is not safe under restart-from-the-top — a charge, an email, an outbound webhook. Before you cut over, go through every side effect and answer one question: what happens if this runs twice? Idempotency keys on the provider side, upserts instead of inserts, and dedupe keys on outbound messages cover almost all of it.

Category four: the cloud-native options

If you are already deep in one cloud and your motivation is consolidation rather than ergonomics, these are real answers and they are frequently dismissed too fast.

AWS Step Functions with EventBridge is the closest structural analogue to what Inngest does: EventBridge routes events, Step Functions runs the durable state machine, and the state machine can wait for a very long time, call Lambda, run tasks on ECS, or wait for a callback token from something external. It is deeply integrated with IAM, CloudWatch and X-Ray, which is either the main attraction or the main objection depending on your team. The costs are that the workflow is defined in ASL rather than in your language — Step Functions is a state machine you configure, not a function you write, and the SDK layers on top only partly hide that — and that the local development story is meaningfully worse than a dev server on your laptop.

Google Cloud Workflows plus Eventarc and Cloud Tasks is the GCP-shaped version, with the same trade: YAML-defined orchestration, excellent integration with the rest of the platform, and a modelling language rather than your programming language. Azure Durable Functions is the interesting outlier here because it does let you write orchestration in ordinary code with an async/await feel, using the same event-sourced replay approach as Temporal — with the same determinism constraints, which people discover late.

My honest read: pick one of these because the rest of your system already lives there and one fewer vendor is worth real money, not because the developer experience will be better. It will not be. What it will be is boring, governed, and paid for on an invoice you already receive.

Side by side

  • Temporal — Model: deterministic workflow code replayed from an event history, with long-lived workers polling task queues. Ops: highest self-hosted; Cloud removes the cluster, never the workers. Choose when orchestration is complex, long-lived and central enough that someone will own it.
  • Restate — Model: a single binary with an embedded log that invokes your handlers, built on durable RPC, durable promises and keyed virtual objects. Ops: lightest of the true engines. Choose when you want Inngest's inversion with the engine on your own machines.
  • DBOS — Model: a library that checkpoints workflow state into your own Postgres, transactionally with your data, with no orchestrator in the request path. Ops: whatever your Postgres already costs. Choose when the objection was data residency or dependency count.
  • Trigger.dev — Model: TypeScript-first long-running tasks executed on their compute, with retries, schedules, concurrency controls and a strong run dashboard; open source and self-hostable. Choose as the closest hosted like-for-like, and verify which features the self-hosted build includes.
  • Hatchet — Model: a Postgres-backed durable task queue with DAG dependencies, fairness and concurrency keys; workers hold a long-lived connection. Choose when the flow-control features are what you would miss most.
  • BullMQ, Celery, River, Sidekiq, pg-boss, Oban — Model: a library plus a store you run, with delays, priorities, retries and schedules, and no replay. Ops: the store, and hand-written idempotency. Choose when the workflows were really just background jobs, which is more often than the category admits.
  • Step Functions + EventBridge, GCP Workflows, Azure Durable Functions — Model: the cloud's own orchestrator, configured in a modelling language (or, for Azure, in replayed code). Ops: the cloud's. Choose for consolidation and governance, not for ergonomics.
  • PandaStack — Model: not an orchestrator; Firecracker microVMs that run the worker process, cron-scheduled functions, and managed Postgres 16 as the store several options above want. Choose for the halves the products above leave to you.

When Inngest is still the right answer

I would keep it, or choose it fresh, in these situations — and I think this list is longer than most competitor-written roundups will tell you.

  1. Your deployment target genuinely cannot run a long-lived process. If everything you ship is a serverless function with a hard execution ceiling and you want to keep it that way, the HTTP-invoked step model is not a workaround, it is the correct design for that constraint. Every alternative except Trigger.dev and the cloud-native ones assumes you can run a poller.
  2. You lean hard on the flow-control layer. Concurrency keyed per customer, throttling against a third-party rate limit, debounce, batching, priority — these are individually easy and collectively a project. If your functions are covered in that configuration, you are not buying durability, you are buying a scheduler, and rebuilding it on a queue library will take longer than you think.
  3. Your team is small and nobody wants to own infrastructure. The operational cost of Temporal, Restate or a self-hosted anything is a real headcount question. A hosted control plane you never think about is worth a lot when there are four engineers.
  4. The event-driven shape is load-bearing. If multiple functions fan out from the same event, and you use event history to debug and replay, you are using the part that is genuinely differentiated, not the part you could get from a job queue.
  5. You are actually waiting days, in many workflows, not two. Durable long waits are the feature that is annoying to build yourself. If a real fraction of your functions need them, keep the engine.
The best reason to leave a step DSL is that you counted your functions and found that most of them never needed one. The worst is a pricing page you read in a bad mood.

Where PandaStack fits, and where it does not

To restate the disclosure: we do not offer a durable execution engine or a hosted job service. If you want a step DSL with a control plane, buy one of the products above. We are relevant to exactly one path out of Inngest — the one where the conclusion is 'what we actually needed was a long-running process' — and irrelevant to the rest.

A PandaStack app is a full Ubuntu userspace inside a Firecracker microVM, deployed from git with blue-green rollouts. That means a worker is just a process: it can hold a Redis or Postgres connection open, prefetch a batch, run for as long as the job takes, spawn a subprocess, shell out to ffmpeg, and drain cleanly on SIGTERM. You can run the web server and the worker in the same sandbox, or deploy the worker as a second app from the same repo with a different start command. Managed Postgres 16 is available if you want the SKIP LOCKED pattern or a DBOS or Hatchet store, and cron-scheduled serverless functions cover the jobs that really are just a timer.

# Two apps, one repo. The web app and the worker are the same code with
# different start commands -- which is the entire deployment story once
# you stop needing a control plane to invoke your steps.

pandastack apps create \
  --name api \
  --git https://github.com/acme/backend \
  --start "node dist/server.js"

pandastack apps create \
  --name worker \
  --git https://github.com/acme/backend \
  --start "node dist/worker.js"

# The worker's env is where the queue lives. Managed Postgres if the queue
# is a table you own; an external Redis if you went with BullMQ.
pandastack apps env set worker \
  DATABASE_URL="postgres://..." \
  REDIS_URL="rediss://..." \
  WORKER_CONCURRENCY=20

# One-off and scheduled work does not need a permanent process at all.
pandastack schedules create nightly-rollup \
  --cron "0 3 * * *" \
  --function rollup
The important caveat, and it cuts against our own headline feature. Our apps scale to zero — restoring a microVM from a snapshot has a create p50 around 179ms, against roughly three seconds for a first cold boot. That is excellent for request traffic and actively wrong for a queue consumer, because a worker that sleeps stops polling, and a queue nobody polls is a backlog with no error and no alert. Run steady-state workers always-on and pay for the process to sit there. Use scale-to-zero for the request-driven half and for disposable per-job sandboxes, not for the poller.

The pricing shape is the other half of why this path appeals to people leaving on cost. We bill compute — $0.054 per vCPU-hour and $0.0162 per GiB-hour — so an idle poll loop costs what the process costs and nothing per step. That is a straightforwardly better shape for a high-step-count, low-CPU workload and a straightforwardly worse one for a workload that runs for four minutes a day, where a per-run price of nearly zero beats paying for a machine to exist. Do that arithmetic with your own numbers rather than trusting either vendor's framing, mine included.

One more place we are useful regardless of which engine you pick: the step that runs code you did not write. If a workflow executes model-generated code, a customer's script, or a migration rehearsal, that step wants its own kernel and a disposable machine, not the worker process holding your durable state and credentials. That is a sandbox concern, not an orchestration concern, and no product in this post provides it.

How to choose, in about fifteen minutes

  1. Count your functions and split them into two piles: ones that need a wait longer than a single request, and ones that are background jobs. If the second pile is most of them, your answer is a queue library and the rest of this list is noise.
  2. Write down whether you can run a long-lived process. If you cannot and will not change that, your shortlist is Inngest, Trigger.dev, and your cloud's own orchestrator. Everything else assumes a poller.
  3. Model the cost with your real step count, not your run count. Then model it again with the step granularity you would use if steps were free, because that difference is the tax the billing model puts on your design.
  4. Name the actual constraint behind 'we want to self-host'. Data residency, vendor risk, and cost are three different problems with three different answers, and only one of them is solved by running the software yourself.
  5. List the flow-control features you use today — concurrency keys, throttle, debounce, batching, priority. Check each one against the candidate. This is where migrations quietly fail, weeks after the happy path works.
  6. Audit every side effect for what happens if it runs twice. You need this before leaving any memoised model, and honestly you needed it anyway.
  7. Decide separately where the worker runs and where the dangerous steps run. Orchestration, compute and isolation are three questions, and bundling them is how teams end up executing untrusted code inside the process that holds their credentials.

The short version

Trigger.dev if you want the closest hosted like-for-like and possibly a self-hosted path. Temporal if orchestration is complex and central enough to deserve an owner. Restate for the same inversion Inngest uses, on a single binary you run. DBOS if you trust your Postgres and would rather add a library than a vendor. Hatchet if flow control is the thing you would miss. Step Functions or GCP Workflows if consolidation beats ergonomics. And BullMQ, Celery, River, Sidekiq, pg-boss or Oban if, having counted, you found that most of your durable functions were background jobs all along.

The conclusion I keep reaching when I help teams with this is uncomfortable for everyone selling something: the step DSL was frequently a workaround for a compute limitation, and once the compute limitation goes away — because the worker is a normal Linux process again — most of the workflows collapse back into ordinary functions with idempotency keys. Keep the engine for the ones that genuinely need to wait three days. Do not keep it for the forty that just needed to run in the background.

Frequently asked questions

What is the best alternative to Inngest?

There is no single answer, because the reasons for leaving point at different products. If you want the closest hosted equivalent with a self-hosting option, Trigger.dev is the usual first stop. If you want the same inverted invocation model but on infrastructure you run, look at Restate. If orchestration is complex and long-lived enough to deserve a dedicated owner, Temporal has the richest primitive set. If your objection was data residency or vendor count, DBOS checkpoints workflow state into your own Postgres as a library rather than a cluster. And if you count your functions and find most are ordinary background jobs rather than multi-day workflows, a queue library like BullMQ, Celery or River is the honest answer.

Can you self-host Inngest?

Parts of the stack are open source and self-hosting material has been published, but you should verify the current licence and exactly what a self-hosted deployment includes before you commit, because 'open source developer tooling with a proprietary control plane' and 'you can run the entire system' are very different propositions and this distinction changes over time in this category. If self-hosting is a hard requirement driven by procurement or data residency, the alternatives with unambiguous self-hosted stories are Temporal, Restate, Hatchet, Trigger.dev and DBOS, with DBOS being the lightest because it is a library on your existing Postgres rather than a service to operate.

Is Inngest expensive at high step volume?

It depends entirely on how you write steps, which is the point worth understanding. When billing is tied to steps and runs rather than to CPU time, the economics reward coarse steps while the programming model rewards fine-grained ones, because fine steps give better crash-recovery granularity. Teams typically notice when a loop over many records becomes a step per record. Do not trust any number in a blog post, including a competitor's: model your real workflow at the step granularity you would naturally write, then again at the coarsest granularity you could tolerate, and check the current pricing page against both. The gap between those two numbers is the design tax the billing model imposes.

Do I need durable execution, or just a job queue?

Count how many of your functions need to pause for longer than a single request — for a human approval, a settlement window, a webhook that arrives tomorrow. If that number is small, you want a job queue with delayed jobs, and durable execution is buying you a programming model, a vendor and an idempotency story you could have written yourself. If a real fraction of your workflows wait for days, have many branching steps, or need an auditable ordered record of what happened, durable execution earns its place. The migration direction matters too: a well-factored queue handler becomes a durable step almost verbatim later, while unwinding an engine four teams have built on is considerably harder.

How do I run a background worker without a step function platform?

Run it as an ordinary long-lived Linux process next to your application. A worker wants to hold connections open, keep a warm pool, prefetch batches, run for as long as the job takes, and drain cleanly on SIGTERM — none of which fits a function runtime with an execution ceiling, which is the constraint that pushed many teams toward step DSLs in the first place. On PandaStack, an app is a full Ubuntu userspace in a Firecracker microVM, so you deploy the worker from the same repo as your web app with a different start command. The one rule: do not put a queue consumer on a scale-to-zero path, because a sleeping worker stops polling and the backlog grows with no error to alert on.

Keep reading

Run code in a microVM in one API call.

49ms p50 cold start. Fork, snapshot, and scale to zero.

Start free
Written by Ajay Kumar, Founder, PandaStack.