Updated Nov 5, 20266 min read

SQS vs Kafka: Queue or Event Log for Your Backend?

Amazon SQS is a managed queue where messages disappear once processed, and Apache Kafka is a partitioned log that keeps events for replay. How they differ on delivery, ordering, retention, throughput and operations, and which one a backend actually needs.

SQS and Kafka both move messages between services, and teams often compare them when the first asynchronous job shows up: sending emails after signup, processing an upload, syncing to a CRM. They are built on different models, and picking the wrong one either means running a Kafka cluster for a job queue or rebuilding on Kafka later because SQS cannot replay events.

Amazon SQS is a managed queue where each message is processed and deleted. Apache Kafka is a distributed, replicated log where events stay for a retention period and many consumers read them independently. This guide compares the two on the points that decide the choice and covers how Encore (infrastructure from code) handles queues for Go and TypeScript services.

SQS vs Kafka at a glance

Encore Pub/Sub (on AWS)Amazon SQSApache Kafka
ModelTopics with independent subscriptionsQueuePartitioned, replicated log
After processingAcknowledged per subscriptionDeleted by the consumerKept until retention expires
Fan-outBuilt in, one queue per subscriptionOne queue per consumer, usually via SNSConsumer groups read the same topic
OrderingOptional per ordering attributePer message group in FIFO queuesPer partition
ReplayNoNoYesby resetting offsets
Failed messagesRetries and a dead-letter queue per subscriptionRedrive policy to a dead-letter queueHandled by the consumer
OperationsProvisioned in your AWS accountFully managedBrokers, or a managed service like MSK
Local developmentencore runLocalStack or a dev queueDocker container or a dev cluster

Pub/Sub on SNS and SQS from Go or TypeScript with Encore

Encore

Encore is infrastructure from code for TypeScript and Go, with a platform that provisions and deploys into your own AWS or GCP account. A topic and its subscriptions are declared in code, and on AWS Encore provisions an SNS topic with an SQS queue and dead-letter queue per subscription, or GCP Pub/Sub with dead-letter topics on GCP, as described in the AWS infrastructure docs.

The Infrastructure page in the Encore dashboard with a database's configuration panel open, showing fields for vCPUs, memory, storage, availability type, max connections and backups.
Each provisioned resource can be reviewed and its configuration edited per environment in the Encore dashboard. The screenshot shows a GCP Cloud SQL database.

Set up the app

Get started
brew install encoredev/tap/encore &&
encore app create
Copy

Run in your terminal to get started locally.

~
$ encore app create shop --example=go/hello-world$ cd shop && mkdir order receipt

The order service publishes an event and the receipt service handles it, with retries and a dead-letter queue configured on the subscription:

// order/order.go package order import ( "context" "encore.dev/pubsub" "encore.dev/storage/sqldb" ) var db = sqldb.NewDatabase("orders", sqldb.DatabaseConfig{ Migrations: "./migrations", }) type OrderPlaced struct { OrderID int64 `json:"order_id"` Email string `json:"email"` } var OrderPlacedTopic = pubsub.NewTopic[*OrderPlaced]("order-placed", pubsub.TopicConfig{ DeliveryGuarantee: pubsub.AtLeastOnce, }) type PlaceParams struct { Email string `json:"email"` } //encore:api public method=POST path=/orders func Place(ctx context.Context, p *PlaceParams) error { var id int64 err := db.QueryRow(ctx, `INSERT INTO orders (email) VALUES ($1) RETURNING id`, p.Email).Scan(&id) if err != nil { return err } _, err = OrderPlacedTopic.Publish(ctx, &OrderPlaced{OrderID: id, Email: p.Email}) return err }
-- order/migrations/1_create_orders.up.sql CREATE TABLE orders ( id BIGSERIAL PRIMARY KEY, email TEXT NOT NULL );
// receipt/receipt.go package receipt import ( "context" "time" "encore.app/order" "encore.dev/pubsub" "encore.dev/rlog" ) var _ = pubsub.NewSubscription(order.OrderPlacedTopic, "send-receipt", pubsub.SubscriptionConfig[*order.OrderPlaced]{ Handler: SendReceipt, RetryPolicy: &pubsub.RetryPolicy{ MinBackoff: 1 * time.Second, MaxRetries: 5, }, }, ) func SendReceipt(ctx context.Context, e *order.OrderPlaced) error { rlog.Info("sending receipt", "order_id", e.OrderID) return nil }

encore run runs both services with an in-memory Pub/Sub, and the local development dashboard traces each message from publish to handler. A handler that returns an error is retried, and after MaxRetries the message goes to the subscription's dead-letter queue, as covered in the Go Pub/Sub docs. The same model exists in TypeScript. Encore provisions an IAM role for the service, so no queue URLs or access keys sit in config.

Encore Pub/Sub is a queue and fan-out system, and it does not keep history for replay. When an AI coding agent adds a new consumer, it writes one more NewSubscription call and tests it locally, and the SQS queue and dead-letter queue for it arrive with the deploy.

Best fit: Go and TypeScript teams who need queues and fan-out between services on AWS or GCP with retries and dead-letter queues, declared in their service code.

Amazon SQS

Amazon SQS is a fully managed queue with no servers or partitions to plan. A consumer polls with long polling, receives up to 10 messages, and deletes each after processing. While a message is being processed it is hidden for the visibility timeout, and if the consumer does not delete it in time it becomes visible again and is retried. A redrive policy moves messages to a dead-letter queue after a set number of receives. Messages can be up to 1 MiB and are kept for up to 14 days.

Standard queues deliver at least once with best-effort ordering and scale with little planning. FIFO queues add ordering within a message group and deduplication, with lower throughput limits. SQS has no fan-out of its own, so publishing one event to several consumers means an SNS topic in front of one queue per consumer.

Good to know

Once a message is deleted it is gone, so SQS cannot rebuild a read model or feed a new consumer with past events. Teams that need that keep an event table in their database or move to a log.

Apache Kafka

Apache Kafka stores events in topics split into partitions, each an ordered, replicated log on the brokers. Producers append, and consumers in a consumer group divide the partitions between them and commit offsets. Events stay until a time or size retention limit, so a new consumer group can read from the beginning, and a broken consumer can be rewound and replayed. Ordering holds within a partition, and throughput scales with partitions and brokers into millions of events per second.

The ecosystem is Kafka's other strength: Kafka Connect moves data to and from databases and warehouses, and Kafka Streams and Flink process streams. Kafka 4.x runs only in KRaft mode without ZooKeeper, and Kafka 4.2 made share groups production-ready, which adds queue-style consumption with per-record acknowledgement.

Good to know

Kafka is a cluster you run: brokers and controllers, partition counts that are hard to reduce later, replication settings, upgrades and monitoring of consumer lag. Amazon MSK runs the brokers on AWS, and the partition planning, client configuration and dead-letter handling stay with you.

Which one to use

Pick SQS for job queues and decoupling services when each message is processed once and then discarded, which covers emails, webhooks, uploads and most background work. Pick Kafka when events are a shared record that several systems read at their own pace, when you need replay, or when throughput is sustained and high enough to justify a cluster. Running both is common, with Kafka for analytics and change data capture and a queue for application jobs.

Pick Encore when you want queues and fan-out between Go or TypeScript services, provisioned as SNS and SQS in your own AWS account from the code that publishes and handles the messages.

Frequently asked questions

What is the difference between SQS and Kafka?

SQS is a managed queue: a consumer receives a message, processes it and deletes it, and the message is gone. Kafka is a distributed log: events are appended to partitioned topics and kept for a retention period, and each consumer group tracks its own offset, so several consumers can read the same events and replay history.

Is SQS cheaper than Kafka?

For most backend workloads, yes. SQS charges per request with no servers to run, while Kafka needs brokers running all the time, either self-managed or through a managed service such as Amazon MSK. Kafka becomes cost-effective at sustained high throughput where per-request pricing adds up.

Does SQS guarantee message order like Kafka?

Standard SQS queues offer best-effort ordering. SQS FIFO queues preserve order within a message group ID, which is similar to Kafka preserving order within a partition.

How do I add a queue between two Go services on AWS without managing SQS directly?

With Encore, declare a topic with pubsub.NewTopic and a handler with pubsub.NewSubscription, and publish with Publish. encore run runs it locally, and on AWS Encore provisions SNS and SQS with a dead-letter queue for the subscription, retrying failed messages according to the RetryPolicy.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders