<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/just-use-postgres-as-a-queue--uqa3ui7eg" -->

---
title: Just Use Postgres as a Queue? | daily.dev
description: Using Postgres as a message queue is possible but trades infrastructure simplicity for implementation complexity. Since databases, Kafka, Redis, and RabbitMQ...
canonical: https://daily.dev/posts/just-use-postgres-as-a-queue--uqa3ui7eg
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Just Use Postgres as a Queue? | daily.dev
og:description: Using Postgres as a message queue is possible but trades infrastructure simplicity for implementation complexity. Since databases, Kafka, Redis, and RabbitMQ...
og:url: https://daily.dev/posts/just-use-postgres-as-a-queue--uqa3ui7eg
og:image: https://api.daily.dev/og/posts/UQA3UI7eg.png
og:image:alt: Just Use Postgres as a Queue?
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Just Use Postgres as a Queue?

**[CodeOpinion](https://daily.dev/sources/codeopinion)** · [@codeopinion](https://daily.dev/codeopinion) · 9 min read · 0 upvotes · 0 comments

## Summary

Using Postgres as a message queue is possible but trades infrastructure simplicity for implementation complexity. Since databases, Kafka, Redis, and RabbitMQ expose different semantics, developers using Postgres as a queue must build visibility timeouts, retries with backoff, dead lettering, ordering, and competing consumer handling themselves using techniques like FOR UPDATE SKIP LOCKED and polling or LISTEN/NOTIFY. The main upside is avoiding the outbox pattern since state and messages are written in one transaction, eliminating the dual-write problem. The recommendation is to understand these trade offs rather than avoid or default to Postgres blindly, and to consider existing messaging libraries that implement these semantics for you.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://codeopinion.com/just-use-postgres-as-a-queue>

## Questions this post answers

### How do you implement competing consumers for a queue built on Postgres?

Use a transaction that selects rows from the queue table with FOR UPDATE SKIP LOCKED, which locks the selected rows and skips any rows already locked by another consumer. After processing, delete the row from the queue and commit the transaction, allowing multiple consumers to safely process different messages concurrently without conflicting on the same row.

_daily.dev surfaces practical patterns like this for developers wiring queue semantics directly into Postgres._

### Why don't you need the outbox pattern if you use Postgres itself as your message queue?

The outbox pattern exists to solve the dual-write problem, where state and a message are written to two separate systems (a database and a broker) and one write can fail without the other. When the queue lives in the same Postgres database as your business data, you persist state and the queued message in a single transaction, so reliable publishing comes for free without needing an outbox table.

_daily.dev helps engineers comparing messaging architectures weigh trade offs like outbox versus in-database queues._

### What complexity do you need to handle yourself if you use Postgres as a message queue instead of a broker like RabbitMQ or Kafka?

You must implement queue semantics manually: visibility timeouts for stalled messages, retry logic with optional backoff, dead letter handling for permanent failures, message ordering under concurrent competing consumers, and replay logic for failed or dead-lettered messages. Polling the database introduces a further trade off between performance impact from frequent polling and latency from infrequent polling, partially mitigated by Postgres LISTEN/NOTIFY.

_daily.dev keeps architects informed on the hidden costs of repurposing databases as queues before committing._

## Similar posts on daily.dev

- [Using PostgreSQL as a Dead Letter Queue for Event-Driven Systems](https://daily.dev/posts/using-postgresql-as-a-dead-letter-queue-for-event-driven-systems-ekm9n1imv) · Hacker News · 0 upvotes · 0 comments
- [Potential Consequences of Using Postgres as a Job Queue](https://daily.dev/posts/potential-consequences-of-using-postgres-as-a-job-queue-kcv0c9q1v) · Lobsters · 70 upvotes · 0 comments
- [How to Use PostgreSQL as a Cache, Queue, and Search Engine](https://daily.dev/posts/how-to-use-postgresql-as-a-cache-queue-and-search-engine-ykurnyxgc) · freeCodeCamp · 90 upvotes · 1 comments
- [Just Use Postgres](https://daily.dev/posts/just-use-postgres-ueffslq3a) · Lobsters · 0 upvotes · 0 comments

---

Tags: [#architecture](https://daily.dev/tags/architecture), [#postgresql](https://daily.dev/tags/postgresql), [#distributed-systems](https://daily.dev/tags/distributed-systems)

[View this post on daily.dev](https://daily.dev/posts/just-use-postgres-as-a-queue--uqa3ui7eg)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Just Use Postgres as a Queue?","url":"https://daily.dev/posts/just-use-postgres-as-a-queue--uqa3ui7eg","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/just-use-postgres-as-a-queue--uqa3ui7eg"},"datePublished":"2026-10-04T13:07:22.095Z","dateModified":"2026-10-05T10:00:18.362Z","description":"Using Postgres as a message queue is possible but trades infrastructure simplicity for implementation complexity. Since databases, Kafka, Redis, and RabbitMQ...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/c2968ae384ef3185eb08a930fe53e0c2?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/c2968ae384ef3185eb08a930fe53e0c2?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"CodeOpinion","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Person","name":"Derek Comartin","url":"https://daily.dev/codeopinion","image":"https://lh3.googleusercontent.com/a-/AOh14GgoQapb7NmLN1TZ6L-UCxkRpDZ4H7LKwud1DUZCTSI=s100","interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"EndorseAction"},"userInteractionCount":950}},"commentCount":0,"discussionUrl":"https://daily.dev/posts/just-use-postgres-as-a-queue--uqa3ui7eg","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"architecture,postgresql,distributed-systems","timeRequired":"PT9M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"CodeOpinion","item":"https://daily.dev/sources/codeopinion"},{"@type":"ListItem","position":3,"name":"Just Use Postgres as a Queue?"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/just-use-postgres-as-a-queue--uqa3ui7eg#faq","mainEntity":[{"@type":"Question","name":"How do you implement competing consumers for a queue built on Postgres?","acceptedAnswer":{"@type":"Answer","text":"Use a transaction that selects rows from the queue table with FOR UPDATE SKIP LOCKED, which locks the selected rows and skips any rows already locked by another consumer. After processing, delete the row from the queue and commit the transaction, allowing multiple consumers to safely process different messages concurrently without conflicting on the same row. daily.dev surfaces practical patterns like this for developers wiring queue semantics directly into Postgres."}},{"@type":"Question","name":"Why don't you need the outbox pattern if you use Postgres itself as your message queue?","acceptedAnswer":{"@type":"Answer","text":"The outbox pattern exists to solve the dual-write problem, where state and a message are written to two separate systems (a database and a broker) and one write can fail without the other. When the queue lives in the same Postgres database as your business data, you persist state and the queued message in a single transaction, so reliable publishing comes for free without needing an outbox table. daily.dev helps engineers comparing messaging architectures weigh trade offs like outbox versus in-database queues."}},{"@type":"Question","name":"What complexity do you need to handle yourself if you use Postgres as a message queue instead of a broker like RabbitMQ or Kafka?","acceptedAnswer":{"@type":"Answer","text":"You must implement queue semantics manually: visibility timeouts for stalled messages, retry logic with optional backoff, dead letter handling for permanent failures, message ordering under concurrent competing consumers, and replay logic for failed or dead-lettered messages. Polling the database introduces a further trade off between performance impact from frequent polling and latency from infrequent polling, partially mitigated by Postgres LISTEN/NOTIFY. daily.dev keeps architects informed on the hidden costs of repurposing databases as queues before committing."}}]}
```

