Before You Add Kafka, Redis, and Elasticsearch: Try One Postgres First A developer in Dhaka built an AI agent infrastructure using a single Postgres database for job queuing, caching, and full-text search instead of adopting Redis, RabbitMQ, and Elasticsearch. The approach, which uses SKIP LOCKED for queues, unlogged tables for cache, and tsvector with GIN indexes for search, is gaining traction as highlighted by the 'PostgreSQL for Everything' essay and examples from Contentful, Instacart, and The Guardian. Last month I was building out my own AI agent infrastructure, the side project where I run a few agents that research, draft, and publish content. The agents needed three things beyond the main database: a job queue, a cache for repeated lookups, and search over article text. My instinct, six years of Spring Boot muscle memory, was to reach for the usual stack. RabbitMQ or Redis Streams for the queue. Redis for the cache. Elasticsearch or Meilisearch for search. Then I counted the services. One Spring Boot app plus three more moving parts, each with its own Docker image, its own failure modes, its own 3 AM pager behavior. For a system serving a handful of agents, that felt like hiring an orchestra to play a ringtone. So I tried something that old-school database people keep telling us and we keep ignoring: I did all three jobs in Postgres. A table with SKIP LOCKED for the queue. An unlogged table for the cache. A tsvector column with a GIN index for search. The whole thing is one schema, one backup strategy, one connection pool. This idea is having a moment again. An essay titled "PostgreSQL for Everything" by Raphael Bauer has been on the Hacker News front page https://www.raphaelbauer.com/posts/postgresql-everything/ this week, and it makes the case I stumbled into: before you adopt a new specialized system, ask whether Postgres already does that job well enough. Contentful rebuilt their full-text search on Postgres https://www.contentful.com/blog/contentful-faster-full-text-search/ instead of a separate search cluster. Instacart built a modern search infrastructure on Postgres https://tech.instacart.com/how-instacart-built-a-modern-search-infrastructure-on-postgres-c528fa601d54 . The Guardian famously migrated off MongoDB onto Postgres https://www.theguardian.com/info/2018/nov/30/bye-bye-mongo-hello-postgres for parts of their platform. Those are big companies. I am one developer in Dhaka with a VPS. But that is exactly the point. The smaller your team, the more a single database that does five jobs is worth. Here is the Spring Boot version of "Postgres for everything", the patterns I actually used, with code. Full disclosure up front: I have run these patterns in my own projects, not at giant scale. I will point out where each one breaks down as you grow, because some of them do. The workhorse. You have a jobs table, workers poll it, and each job must be claimed by exactly one worker. The naive approach, SELECT then UPDATE WHERE status = 'pending' , races between workers. Postgres has had the fix since 9.5: SELECT ... FOR UPDATE SKIP LOCKED . Each worker locks the rows it grabs, and rows already locked by another worker are simply skipped, so two workers never claim the same job. No advisory locks, no leader election, no broker. The Postgres docs cover this under row-level locking https://www.postgresql.org/docs/current/explicit-locking.html LOCKING-ROWS , and Crunchy Data has a good deep dive on queuing with native Postgres https://www.crunchydata.com/blog/message-queuing-using-native-postgresql . The entity: @Entity @Table name = "jobs", indexes = @Index name = "idx jobs pending", columnList = "status, runAt" public class Job { @Id @GeneratedValue strategy = GenerationType.UUID private UUID id; private String type; // "draft-article", "fetch-stats", ... private String status; // pending, running, done, failed @Column columnDefinition = "jsonb" private String payload; private Instant runAt; private Instant lockedAt; private int attempts; } The claim query, as a native query in your Spring Data repository: public interface JobRepository extends JpaRepository