- QStash pushes. Your existing HTTP route is the consumer. There is no worker process, no poller, no long-lived connection, and nothing to keep running.
- BullMQ, Kafka, and SQS pull. Something you deploy and operate must connect, poll, and stay alive to receive messages.
- SNS and Pub/Sub can also push to any public HTTPS endpoint, but with much weaker per-message failure handling, and no delay or scheduling at all.
Summary
BullMQ
BullMQ is an open source job queue for Node.js and Python, built on Redis. It’s a library, not a service: you install it, point it at a Redis instance, and run worker processes. What you have to operate. BullMQ needs a Redis instance you keep healthy, backed up, and sized for your peak, plus at least one worker process running continuously. QStash has neither. You publish over HTTP and QStash calls your endpoint. Serverless. This is the sharp edge. A BullMQWorker holds a blocking Redis
connection and processes jobs in a loop — that model does not survive on Vercel,
Lambda, or Cloudflare Workers, so teams end up running a separate always-on box
just for the queue. With QStash there is nothing to keep alive; a message arrives
as an ordinary HTTP request to a route you already deploy.
Cost shape. BullMQ itself is free, but the Redis instance and the worker
container are billed by the hour whether or not you have jobs. QStash is billed per
message and scales to zero.
Where BullMQ still wins. It is in-process, so per-job latency is very low, and
job payloads never leave your network. If you already run persistent Node servers
and a Redis you maintain anyway, BullMQ is a reasonable fit — and its parent/child
flows are more granular than message chaining. For dependent multi-step work on
QStash, use Upstash Workflow instead of hand-chaining
messages.
Feature overlap. Both support delayed jobs, cron/repeatable jobs, retries with
backoff, rate limiting, and concurrency caps. The difference is where those run:
BullMQ enforces them inside your worker, QStash enforces them before the request
ever reaches you, so an overloaded downstream never sees the traffic.
Kafka
Apache Kafka is a distributed, partitioned, replicated commit log — self-hosted, or managed as Amazon MSK, Confluent Cloud, or Redpanda. It is a genuinely different category of system, and for a large class of workloads it’s the wrong tool by a wide margin. Kafka is a log, QStash is a delivery service. Kafka retains an ordered stream that many independent consumer groups read at their own offsets, and can re-read from the beginning. QStash tracks each message individually through delivery, retries, and failure. If you need replay of a stream, event sourcing, or the same events consumed by analytics and a service and a data warehouse, that’s Kafka. Operational weight. Even managed, Kafka means brokers, partitions, replication factors, consumer groups, offset management, rebalancing, and a consumer process that stays connected. The smallest sensible production cluster costs meaningfully more per month than most teams’ entire QStash bill, and the price never scales to zero. QStash has no cluster, no partitions, and no consumers to operate. Per-message control. Kafka has no built-in concept of retrying an individual message, delaying one for three days, or moving one to a dead letter queue — you build all of that yourself with retry topics and custom consumer logic. In QStash these are per-message headers. Ordering. Kafka orders within a partition and scales by adding partitions. QStash queues deliver FIFO and only start the next message once the current one is delivered or has exhausted its retries. Kafka’s throughput ceiling is far higher; QStash’s ordering guarantee is stricter and requires no partition key design. Choose Kafka when you have sustained high-volume event streams, multiple independent consumers of the same data, or a real need for replay and log retention. Choose QStash when each message is a unit of work that should result in exactly one HTTP call to your service.Amazon SQS
Amazon SQS is AWS’s managed queue. Like QStash it is fully managed and scales to zero, so the comparison is about the delivery model and the surrounding features rather than about operations. Pull vs push. SQS holds messages until something polls for them. That something is a consumer you deploy — an ECS task, an EC2 process, or a Lambda with an event source mapping. QStash delivers straight to your URL, anywhere on the internet, in any cloud. There is nothing to wire up and nothing that has to live in AWS. Failure handling. SQS uses visibility timeouts: a failed message reappears after the timeout and is retried untilmaxReceiveCount, then moves to a
dead letter queue you created and attached yourself
— and which you then have to consume too. QStash retries with exponential backoff
by default, lets you override the backoff expression per message,
and gives every project an automatic DLQ you can inspect and
replay from the console.
Delay. SQS message timers max out at 15 minutes. QStash delays go up to
1 year on pay-as-you-go, which covers trial expirations, reminders, and
scheduled emails that SQS simply cannot express.
Scheduling. SQS has no cron. You add EventBridge Scheduler and a Lambda.
QStash schedules are a cron expression on a publish
call, with timezone support.
Rate limiting. SQS has no way to say “call this endpoint at most 10 times a
minute, 5 at a time.” You build it with reserved concurrency and back-pressure
logic. QStash Flow Control does it per key, across
multiple URLs.
Ordering and throughput. SQS FIFO queues are limited to 300 transactions per
second per partition (3,000 messages/s with batching) unless you enable high
throughput mode; standard queues are effectively unlimited but only best-effort
ordered. If you need six-figure message rates inside AWS, SQS is the stronger
choice.
Choose SQS when your producers and consumers all live in AWS, you already run
Lambda or ECS consumers, and you want the deepest possible AWS integration.
Choose QStash when your consumers are HTTP endpoints on serverless platforms,
or you want retries, DLQ, cron, long delays, and rate limiting without assembling
four AWS services.
Amazon SNS
Amazon SNS is AWS’s pub/sub notification service. It pushes, which makes it the closest architectural match to QStash on this list — but it is built for fan-out notification, not for reliable job execution. SNS does not store messages. It attempts delivery to each subscriber and then forgets. If a subscriber is down for the whole retry window, the message is discarded unless you attached an SQS dead letter queue to that subscription. QStash persists every message, retries it, and keeps whatever finally failed in the DLQ. The retry window is capped at one hour. For HTTP/S endpoints, the default delivery policy is only 3 retries, and even a fully customized policy cannot exceed 3,600 seconds total — a hard AWS limit. A deploy that takes 90 minutes to fix loses the message. QStash’s default backoff already stretches retries across roughly a day, and you can set both the retry count and the delay expression per message. Payload size. SNS caps messages at 256 KB. QStash allows 1 MB on the free plan and up to 50 MB on fixed plans. No delay, no cron, no ordering. SNS has none of these. QStash has all three. Fan-out. This is what SNS is for, and QStash’s equivalent is URL Groups: publish once, and QStash creates an independent, independently-retried delivery for each subscribed endpoint. Adding a consumer is a URL Group change, not a producer redeploy. SNS goes further on non-HTTP targets — SQS, Lambda, Kinesis Firehose, email, SMS, and mobile push — and its message filtering lets subscribers select by attribute, which QStash does not do. Choose SNS when you’re fanning out inside AWS, especially to SQS queues, Lambda, SMS, or mobile push. Choose QStash when the subscribers are HTTP endpoints and you need the message to actually survive a bad hour.Google Pub/Sub
Google Cloud Pub/Sub is GCP’s managed messaging service. It supports both pull subscriptions and push subscriptions that POST to an HTTPS endpoint, so it overlaps with QStash more than SQS does. Scope. Pub/Sub is a high-throughput event distribution backbone — it sits closer to Kafka than to a job queue, with 10 MB messages, seek-and-replay within the retention window (7 days by default, up to 31 days at the topic level), and snapshots. QStash is scoped to reliable per-message delivery of work. Per-message control. Pub/Sub configures retry policy, ack deadlines, and dead letter topics per subscription. Every message on a subscription gets the same treatment. In QStash, retries, backoff, delay, timeout, and flow control are set per message, so a heavy job and a trivial one can share the same endpoint with different policies. Delay and scheduling. Pub/Sub has neither. There is no per-message delay and no cron; scheduled work means adding Cloud Scheduler as a separate service. QStash has delays up to a year and cron schedules built in. Rate limiting. Pub/Sub flow control is configured in the subscriber client library — it protects the subscriber, and only works if you run a subscriber process. Push subscriptions get a delivery rate that Pub/Sub adjusts on its own. QStash Flow Control is enforced server-side before delivery, with an explicit rate, period, and parallelism you choose. Setup. A Pub/Sub push subscription lives inside a GCP project: a topic, a subscription, and — if you want the endpoint authenticated — a service account whose OIDC token your handler validates. QStash needs a token and, optionally, signature verification with a one-line SDK call. Choose Pub/Sub when you’re on GCP, need very high throughput, replay, or schema-validated topics, and want native integration with Dataflow and BigQuery. Choose QStash when you want per-message reliability controls and scheduling without a cloud project behind it.Use Cases
What people actually build with QStash
Getting Started
Publish your first message in a few minutes
Serverless Background Jobs and Message Queues Compared
A wider survey, including Cloud Tasks, Inngest, and Trigger.dev
Pricing & Limits
Per-plan message sizes, delays, retention, and cost