Can I use Postgres as a message queue?

Yes. Every SupaHost project can enable Queues (pgmq), a Postgres-native message queue with visibility timeouts and archiving, managed from the dashboard's Queues tab.

Yes. SupaHost projects include Queues, built on the pgmq extension. Messages are rows in Postgres tables, so sending a message can be part of the same transaction as the data change that caused it, and nothing extra has to run. Turn it on in Dashboard → your project → Queues tab → Enable Queues.

How pgmq works

You send a JSON message to a named queue. A consumer reads one or more messages, which hides them for a visibility timeout (for example 30 seconds). If the consumer finishes, it deletes or archives the message. If it crashes, the timeout runs out and the message becomes visible again for another try. That gives at-least-once delivery, so make your handlers safe to run twice.

Example: sending receipts for an invoice tool

Create a queue (or use Queues tab → new queue):

select pgmq.create('receipts');

Enqueue a message in the same transaction that marks an invoice paid:

begin;
update public.invoices set status = 'paid', paid_at = now() where id = 4211;
select pgmq.send('receipts', jsonb_build_object('invoice_id', 4211));
commit;

Consume from a server or Edge Function with the service_role key, through the pgmq_public wrappers that the Data API exposes:

const { data: msgs, error } = await admin.schema('pgmq_public').rpc('read', {
  queue_name: 'receipts',
  sleep_seconds: 30, // visibility timeout
  n: 10,
})

for (const m of msgs ?? []) {
  await sendReceipt(m.message.invoice_id)
  await admin.schema('pgmq_public').rpc('archive', { queue_name: 'receipts', message_id: m.msg_id })
}

To process the queue regularly, call that function from a pg_cron job (see How do I run background jobs?).

SupaHost specifics

  • pgmq_public has send, send_batch, read, pop, archive and delete. Execute is granted to service_role only. If you want signed-in users to send messages from the browser, add the grants and RLS on the queue table yourself.
  • The Queues tab shows Waiting, Total sent and Oldest per queue, the newest 25 messages, and Purge and Delete actions.
  • "Unlogged" queues are faster but aren't crash-safe: messages can be lost if Postgres restarts unexpectedly.

FAQ

Is Postgres fast enough for a queue?

For most apps, yes: emails, webhooks, image jobs and similar background work come nowhere near Postgres's limits, and the queue shares your instance's resources with everything else. If you need millions per second or fan-out to many independent consumers, a dedicated broker is a better fit.

Why do my messages come back after I read them?

The visibility timeout expired before you deleted or archived them. Use a longer timeout or finish faster. See Queue messages reappearing.

Can I build my own queue with SKIP LOCKED instead?

Yes. A jobs table read with select ... for update skip locked limit 10 works well, and you'd call it through a database function. pgmq saves you writing that yourself.

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us