Your Database Is Already a Better Message Queue Than Kafka. Here’s Why.

You’ve been there. The architecture review where someone says, “We need a message queue. Let’s add Kafka.” Everyone nods. Another service, another cluster, another late-night pager duty. It feels inevitable. But it’s not. It’s a lie we’ve been telling ourselves for a decade.

Conventional wisdom says databases can’t handle queues. That was true in 2012. Today, it’s the most expensive fairy tale in engineering.

I’ve watched teams add Redis, RabbitMQ, and Kafka to projects that could have been solved with a single Postgres table and a smart index. They didn’t need the complexity. They needed permission to trust the tool they already had.

Here’s the uncomfortable truth: Every time you reach for a separate message broker, you’re trading transactional consistency for a problem you might not have. You lose the ability to BEGIN and COMMIT across your queue and your data. You introduce a new point of failure, a new learning curve, and a new operational drag. For what? The promise of infinite scale that 99% of teams never need.

Let me show you the numbers. Modern Postgres, with features like SKIP LOCKED, LISTEN/NOTIFY, and efficient index-only scans, can handle tens of thousands of messages per second on modest hardware. That’s the throughput of a busy e-commerce checkout flow, a real-time notification system, or a job queue for a mid-sized SaaS. Rails already proved it: SolidQueue runs on Postgres by default. It’s been in production for years.

“Postgres doesn’t scale for queues” is the excuse you use when you haven’t tried. The real reason people reach for Kafka is FOMO. Someone else did it, so you must too. But the cost is real: operational complexity, data inconsistency, and a team that has to learn two systems instead of one.

I’m not saying Kafka is useless. For event streaming at massive scale—think millions of messages per second, long retention, replay—it’s the right tool. But for the vast majority of applications—task queues, background jobs, simple pub/sub—Postgres is not just good enough. It’s better. Because it’s already there. It’s already transactional. It’s already backed up.

Here’s the twist: The industry’s obsession with decoupling everything has created more coupling than ever. You’re coupling your team to a new database, a new protocol, a new operational burden. Meanwhile, the simple Postgres-backed queue is the real decoupling—from complexity, from overhead, from the lie that you need to be Netflix before you have 100 users.

So next time someone says, “We need a message queue,” ask them: “Does our queue need to be separate from our data?” If the answer is no, you already have the answer. It’s in your database. It’s been waiting for you.

Stop adding infrastructure. Start using what you already own.

FAQ

Q: What about high-throughput scenarios like millions of messages per second?

A: If you're truly at that scale, Kafka or similar may be justified. But 99% of teams never reach that threshold. For most production workloads, Postgres handles it. Test your own traffic before assuming you need a separate broker.

Q: Isn't polling a database inefficient compared to a push-based broker?

A: Modern Postgres has LISTEN/NOTIFY for push-based notifications, and SKIP LOCKED for efficient polling without blocking. The overhead is negligible for most applications. The real inefficiency is adding a whole new system you don't need.

Q: Are you saying Kafka is useless?

A: No. Kafka is powerful for event streaming, long retention, and replay at massive scale. But for a simple queue or job processing, it's overkill. The contrarian take is: most teams would be better off starting with Postgres queues and only adding Kafka when they have a proven need.

📎 Source: View Source