p.enthalabs

1M Durable Workflows per Second on Postgres

!Image 4: logo

[](https://www.cookiebot.com/en/what-is-behind-powered-by-cookiebot/?utm_source=banner_cb&utm_medium=referral&utm_content=v2)

- Consent

- Details

- [[#IABV2SETTINGS#]](https://www.dbos.dev/blog/one-million-workflows#)

- About

This website uses cookies

We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners who may combine it with other information that you’ve provided to them or that they’ve collected from your use of their services.

Show more

[#GPC_BANNER_ICON#]

[#GPC_TOAST_TEXT#]

Consent Selection

**Necessary**

- [x]

**Preferences**

- [x]

**Statistics**

- [x]

**Marketing**

- [x]

Show details

Details

* Necessary 17- [x] Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.

- Cookiebot 2Learn more about this provider![Image 5: opens in a new window](https://www.cookiebot.com/goto/privacy-policy/ "Learn more about this provider Cookiebot's privacy policy - opens in a new window")**CookieConsent[x2]**Stores the user's cookie consent state for the current domain**Maximum Storage Duration**: 1 year**Type**: HTTP Cookie

- HubSpot 1Learn more about this provider![Image 6: opens in a new window](https://legal.hubspot.com/privacy-policy "Learn more about this provider HubSpot's privacy policy - opens in a new window")**cookietest**This cookie is used to determine if the visitor has accepted the cookie consent box.**Maximum Storage Duration**: Session**Type**: HTTP Cookie

- LinkedIn 2Learn more about this provider![Image 7: opens in a new window](https://www.linkedin.com/legal/privacy-policy "Learn more about this provider LinkedIn's privacy policy - opens in a new window")**bcookie**Used in order to detect spam and improve the website's security. **Maximum Storage Duration**: 1 year**Type**: HTTP Cookie **li_gc**Stores the user's cookie consent state for the current domain**Maximum Storage Duration**: 180 days**Type**: HTTP Cookie

- hs-analytics.net hs-banner.com hubspotusercontent-na1.net linkedin.com 12**__cf_bm[x12]**This cookie is used to distinguish between humans and bots. This is beneficial for the website, in order to make valid reports on the use of their website.**Maximum Storage Duration**: 1 day**Type**: HTTP Cookie

* Preferences 2- [x] Preference cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.

- Cookiebot 1Learn more about this provider![Image 8: opens in a new window](https://www.cookiebot.com/goto/privacy-policy/ "Learn more about this provider Cookiebot's privacy policy - opens in a new window")**CookieConsentBulkSetting-#**Enables cookie consent across multiple websites**Maximum Storage Duration**: 1 year**Type**: HTML Local Storage

- LinkedIn 1Learn more about this provider![Image 9: opens in a new window](https://www.linkedin.com/legal/privacy-policy "Learn more about this provider LinkedIn's privacy policy - opens in a new window")**lidc**Registers which server-cluster is serving the visitor. This is used in context with load balancing, in order to optimize user experience. **Maximum Storage Duration**: 1 day**Type**: HTTP Cookie

* Statistics 6- [x] Statistic cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.

- HubSpot 4Learn more about this provider![Image 10: opens in a new window](https://legal.hubspot.com/privacy-policy "Learn more about this provider HubSpot's privacy policy - opens in a new window")**__hssc**Identifies if the cookie data needs to be updated in the visitor's browser.**Maximum Storage Duration**: 1 day**Type**: HTTP Cookie **__hssrc**Used to recognise the visitor's browser upon reentry on the website.**Maximum Storage Duration**: Session**Type**: HTTP Cookie **__hstc**Sets a unique ID for the session. This allows the website to obtain data on visitor behaviour for statistical purposes.**Maximum Storage Duration**: 180 days**Type**: HTTP Cookie **hubspotutk**Sets a unique ID for the session. This allows the website to obtain data on visitor behaviour for statistical purposes.**Maximum Storage Duration**: 180 days**Type**: HTTP Cookie

- Matomo 2Learn more about this provider![Image 11: opens in a new window](https://matomo.org/privacy-policy/ "Learn more about this provider Matomo's privacy policy - opens in a new window")**_pk_id#**Collects statistics on the user's visits to the website, such as the number of visits, average time spent on the website and what pages have been read.**Maximum Storage Duration**: 1 year**Type**: HTTP Cookie **_pk_ses#**Used by Piwik Analytics Platform to track page requests from the visitor during the session.**Maximum Storage Duration**: 1 day**Type**: HTTP Cookie

* Marketing 14- [x] Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.

- Google 2Learn more about this provider![Image 12: opens in a new window](https://business.safety.google/privacy/ "Learn more about this provider Google's privacy policy - opens in a new window")Some of the data collected by this provider is for the purposes of personalization and measuring advertising effectiveness. The provider may use the IP Addresses for ads measurement and ads personalization.

**_ga**Used to send data to Google Analytics about the visitor's device and behavior. Tracks the visitor across devices and marketing channels.**Maximum Storage Duration**: 2 years**Type**: HTTP Cookie **_ga_#**Used to send data to Google Analytics about the visitor's device and behavior. Tracks the visitor across devices and marketing channels.**Maximum Storage Duration**: 2 years**Type**: HTTP Cookie

- HubSpot 1Learn more about this provider![Image 13: opens in a new window](https://legal.hubspot.com/privacy-policy "Learn more about this provider HubSpot's privacy policy - opens in a new window")**__ptq.gif**Sends data to the marketing platform Hubspot about the visitor's device and behaviour. Tracks the visitor across devices and marketing channels.**Maximum Storage Duration**: Session**Type**: Pixel Tracker

- YouTube 11Learn more about this provider![Image 14: opens in a new window](https://business.safety.google/privacy/ "Learn more about this provider YouTube's privacy policy - opens in a new window")**__Secure-ROLLOUT_TOKEN**Used to track user’s interaction with embedded content.**Maximum Storage Duration**: 180 days**Type**: HTTP Cookie **__Secure-YEC**Stores the user's video player preferences using embedded YouTube video**Maximum Storage Duration**: Session**Type**: HTTP Cookie **__Secure-YNID**Used to track user’s interaction with embedded content.**Maximum Storage Duration**: 180 days**Type**: HTTP Cookie **LAST_RESULT_ENTRY_KEY**Used to track user’s interaction with embedded content.**Maximum Storage Duration**: Session**Type**: HTTP Cookie **LogsDatabaseV2:V#||LogsRequestsStore**Used to track user’s interaction with embedded content.**Maximum Storage Duration**: Persistent**Type**: IndexedDB **ServiceWorkerLogsDatabase#SWHealthLog**Necessary for the implementation and functionality of YouTube video-content on the website. **Maximum Storage Duration**: Persistent**Type**: IndexedDB **TESTCOOKIESENABLED**Used to track user’s interaction with embedded content.**Maximum Storage Duration**: 1 day**Type**: HTTP Cookie **VISITOR_INFO1_LIVE**Tries to estimate the users' bandwidth on pages with integrated YouTube videos.**Maximum Storage Duration**: 180 days**Type**: HTTP Cookie **YSC**Registers a unique ID to keep statistics of what videos from YouTube the user has seen.**Maximum Storage Duration**: Session**Type**: HTTP Cookie **yt-icons-last-purged**Necessary for the implementation and functionality of YouTube video-content on the website. **Maximum Storage Duration**: Persistent**Type**: HTML Local Storage **YtIdbMeta#databases**Used to track user’s interaction with embedded content.**Maximum Storage Duration**: Persistent**Type**: IndexedDB

- Unclassified 7 Unclassified cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.

- Amazon 6Learn more about this provider![Image 15: opens in a new window](https://www.amazon.com/gp/help/customer/display.html/ref=footer_privacy?ie=UTF8&nodeId=468496 "Learn more about this provider Amazon's privacy policy - opens in a new window")**__test__**Pending**Maximum Storage Duration**: Session**Type**: HTTP Cookie **_reb2bgeo**Pending**Maximum Storage Duration**: Session**Type**: HTTP Cookie **_reb2bloaded**Pending**Maximum Storage Duration**: Session**Type**: HTTP Cookie **_reb2bsessionID**Pending**Maximum Storage Duration**: Session**Type**: HTTP Cookie **_reb2buid**Pending**Maximum Storage Duration**: Session**Type**: HTTP Cookie **_reb2buid**Pending**Maximum Storage Duration**: Persistent**Type**: HTML Local Storage

- HubSpot 1Learn more about this provider![Image 16: opens in a new window](https://legal.hubspot.com/privacy-policy "Learn more about this provider HubSpot's privacy policy - opens in a new window")**hubspot-modern-theme**Pending**Maximum Storage Duration**: Persistent**Type**: HTML Local Storage

Cross-domain consent 2

Your consent applies to the following domains:

List of domains your consent applies to:console.dbos.devwww.dbos.dev

Cookie declaration last updated on 10/27/26 by Cookiebot

[#IABV2_TITLE#]

[#IABV2_BODY_INTRO#]

[#IABV2_BODY_LEGITIMATE_INTEREST_INTRO#]

[#IABV2_BODY_PREFERENCE_INTRO#]

[#IABV2_LABEL_PURPOSES#]

[#IABV2_BODY_PURPOSES_INTRO#]

[#IABV2_BODY_PURPOSES#]

[#IABV2_LABEL_FEATURES#]

[#IABV2_BODY_FEATURES_INTRO#]

[#IABV2_BODY_FEATURES#]

[#IABV2_LABEL_PARTNERS#]

[#IABV2_BODY_PARTNERS_INTRO#]

[#IABV2_BODY_PARTNERS#]

About

Cookies are small text files that can be used by websites to make a user's experience more efficient.

The law states that we can store cookies on your device if they are strictly necessary for the operation of this site. For all other types of cookies we need your permission.

This site uses different types of cookies. Some cookies are placed by third party services that appear on our pages.

You can at any time change or withdraw your consent from the Cookie Declaration on our website.

Learn more about who we are, how you can contact us and how we process personal data in our Privacy Policy.

Please state your consent ID and date when you contact us regarding your consent.

- [x]

**Do not sell or share my personal information**

Deny Allow selection Customize Allow all

Join us Nov 12 for Durable Stories Vol. 1: Postgres, Postgres Everywhere

[](https://www.dbos.dev/blog/one-million-workflows#)

![Image 17: DBOS - Logo](https://www.dbos.dev/)

Products

DBOS Transact Open source durable execution libraryDBOS Conductor Control plane for agents & workflows

PricingCustomers

Resources

About DBOS Meet the team simplifying reliability.Videos Demos, deep dives, and more DBOS.Partners Explore the DBOS ecosystem.

Docs

Quickstart Launch your first workflow in minutes.Docs Step-by-step guides & real-world use cases.Example Applications Ready-to-run code to spark your project.

Blog

[](https://discord.com/invite/jsmC6pXGgX)

Explore

5K

dbos/dbos-transact-py Durable Execution Library - Pythondbos/dbos-transact-ts Durable Execution Library - TypeScriptdbos-inc/dbos-transact-go Durable Execution Library - Godbos/dbos-transact-rust Durable Execution Library - Rustdbos-inc/dbos-transact-java Durable Execution Library - Javadbos-inc/dbos-demo-apps Demo DBOS applicationsSee all repos

Sign inGet started

Back to posts

1M Durable Workflows per Second on Postgres

!Image 18

Peter Kraft

October 28, 2026

Postgres

!Image 19

![Image 20 Add to Google](https://www.google.com/preferences/source?q=https://www.dbos.dev)

Share this post

[](https://www.linkedin.com/shareArticle?mini=true&url=https://www.dbos.dev/blog/one-million-workflows&title=1M%20Durable%20Workflows%20per%20Second%20on%20Postgres)

[](https://twitter.com/intent/tweet?url=https://www.dbos.dev/blog/one-million-workflows&hashtags=dbos)

[](https://www.facebook.com/sharer/sharer.php?u=https://www.dbos.dev/blog/one-million-workflows)

[](mailto:?subject=Check%20out%20this%20article%20from%20DBOS&body=Check%20out%20this%20article%20from%20DBOS%3A%0Ahttps://www.dbos.dev/blog/one-million-workflows)

When building a durable workflow engine on Postgres, one of the most common questions we get is “does Postgres scale?” Now, there has been no shortage of blog posts on the subject (including ours), and at this point it’s beating a dead horse to say that a single Postgres database can get you remarkably far, handling tens of thousands of operations per second.

But what if you want to go past that? What if you have a workload that requires more than one database can handle, perhaps hundreds of thousands or millions of operations per second? Can Postgres scale then? In this blog post, we’ll show how we scaled Postgres-backed workflows to >1M workflow/sec.

1M Workflows/Sec

We’ll start with the good part: the results. By sharding durable workflows (using PgDog, but similar results are achievable with any sharding engine), we linearly scaled durable workflow throughput from 20K workflows/sec with one Postgres server to 1M workflows/sec with 50 Postgres servers:

![Image 21: Chart - durable workflow throughput scales linearly from 20K workflows/sec on 1 shard to 1M workflows/sec on 50 shards](https://www.dbos.dev/assets/cms/4841693b75d583e61d68d3e9_1m-workflows-image2.png)

Here’s the benchmark design:

- We scale the number of shards from 1 to 50. Each shard is a db.m7i.4xlarge RDS Postgres with 16 cores and 64 GB of RAM.

- We scale worker processes across several client hosts. We launch 48 worker processes per shard, for a total of 2400 worker processes at 50 shards. All worker processes are identical. Each worker process connects to a PgDog router on its host.

- Worker processes enqueue 20K workflows/shard/sec, up to 1M workflows/sec at 50 shards. We chose this number because 20K workflows/sec is the maximum sustainable throughput of a single shard. The benchmark uses simple no-op workflows with no steps.

- Worker processes enqueue all workflows into a single durable queue. Each workflow has a uniformly random shard key, so workflows are evenly distributed across shards. All worker processes dequeue and execute workflows from this single queue.

- All benchmark code is open-source on GitHub.

How Sharding Works

Because Postgres is a single-node database, the only way to scale to >1M workflows/sec (well beyond the capacity of a single server, however large), is to shard it. Sharding is a technique for horizontally scaling a single-node database by distributing work across many database servers, called “shards”. There are many popular sharding engines: we used PgDog because it’s lightweight and open-source; other Postgres engines include Citus, Neki, and Multigres, and maybe the most popular sharding engine overall is Vitess for MySQL.

Sharding engines are powerful because they distribute work across many servers but present users with the abstraction that they’re working with a single database. To make this work, users select a “shard key” column for each of their tables. The sharding engine partitions each table by its shard key, creating an instance of each table on each shard and assigning data items to shards based on their shard keys. Data items with the same shard keys are always assigned to the same shard, while data items with different shard keys could be stored on different shards. At a high level, sharding looks like this:

![Image 22: Diagram - a sharding engine parses a query, routes it by shard key, and executes it on the matching Postgres shard](https://www.dbos.dev/assets/cms/4841693b75d583e61d68d3e9_1m-workflows-image3.png)

The core of a sharding engine is a **router** that parses queries and distributes them across the appropriate shards. If the query only needs data from a single shard (for example a SELECT query that specifies a single shard key value), that’s easy: the engine routes the query to that single shard and serves its response. If the query needs data from many shards (for example, an aggregation that does not specify a shard key), that’s harder: the engine must construct a distributed query plan that retrieves data from all those shards then combines their responses.

To make the performance of a sharded workload scale linearly, we must ensure each query can be served by a single shard. Otherwise, coordination between shards limits performance. Thus, to shard workflows, we add a `shard_key` column both to the main `workflow_status` table and to all dependent tables (such as the `workflow_steps` describing each workflows’ steps). This way, all information about a single workflow is stored on a single shard, and all operations done on a single workflow (such as enqueueing it, recording a step checkpoint, or sending a message to it) can be served by a single shard. Similarly, we dequeue workflows by shard: instead of dequeuing workflows from all shards at once, each process iterates through all shards, dequeueing workflows from one shard at a time.

Metastability and Feedback Loops

When we initially tested sharded workflows, we found that past a certain point (roughly 8-16 shards, or ~200K workflows/sec), performance was metastable: it would initially appear to scale linearly, but if any shard was perturbed in any way (for example, a large VACUUM briefly slowed down the shard), cluster-wide performance would collapse and never recover.

The reason for this metastability was a negative feedback loop in dequeueing workflows. The query for finding workflows to dequeue looks like the SQL below. Each process periodically polls each queue and shard, finding a number (determined by flow control) of eligible enqueued workflows on that queue and shard and dequeuing them. To minimize contention between workers, the query uses a locking clause (`FOR UPDATE SKIP LOCKED`), locking the workflows it selects and selecting the first unlocked workflows in the queue.

!Image 23: Dequeue SQL statement using FOR UPDATE SKIP LOCKED

The issue with this query is that it is susceptible to a negative feedback loop at a high process count. Let’s say some event (such as a large VACUUM) causes a shard to momentarily slow down, so that each dequeue query takes longer. Because each dequeue query takes longer, more processes concurrently attempt to dequeue from that shard. The problem is that if more processes try to dequeue from a shard, queries on that shard take even longer (due to lock contention, earlier queries hold locks so newer queries have to search ahead to find unlocked workflows). This loop feeds back on itself until millions of rows are locked by queries taking several seconds each, collapsing performance.

![Image 24: Diagram - a slow shard triggers a feedback loop of slower dequeues, more dequeuers, and more lock contention, collapsing the cluster](https://www.dbos.dev/assets/cms/4841693b75d583e61d68d3e9_1m-workflows-image5.png)

To fix this issue (and others like it), we had to stop the negative feedback loop by limiting concurrent accesses to a single shard. We did this using Postgres advisory locks. Each shard has a small number of “dequeue locks”. To dequeue from the shard, a process has to obtain one of the locks. If it can’t obtain a lock, it doesn’t wait, but rather moves on to the next shard and tries to dequeue from it instead. This design prevents a single shard from being overwhelmed, breaking the feedback loop and allowing stable scaling to 1M workflows/sec.

![Image 25: Diagram - each shard has a fixed number of dequeue locks; when all are held, a process moves on to the next shard](https://www.dbos.dev/assets/cms/4841693b75d583e61d68d3e9_1m-workflows-image1.png)

Learn More

If you like building scalable, reliable systems, we’d love to hear from you. At DBOS, our goal is to make Postgres-backed durable execution as simple and performant as possible. Check it out:

- Quickstart:https://docs.dbos.dev/quickstart

- GitHub:https://github.com/dbos-inc

- Discord community: https://discord.gg/eMUHrvbu67

Insights

Recent articles

The latest in durable execution, AI workflows &more.

[](https://www.dbos.dev/blog/whats-new-in-dbos-october-2026)

!Image 26

Product news

Oct 29, 2026

What's New in DBOS - October 2026

Workflow rewind, safer distributed execution, DBOS Transact Enterprise, and scaling to 1M+ workflows per second on sharded Postgres!

!Image 27

Qian Li

[](https://www.dbos.dev/blog/applications-are-dead-long-live-workflows)

!Image 28

How To

Oct 9, 2026

‍Applications are Dead. Long Live Workflows

Applications should be replaced by workflows as the unit of middleware management

!Image 29

Mike Stonebraker

[](https://www.dbos.dev/blog/whats-new-in-dbos-september-2026)

!Image 30

Product news

Sep 29, 2026

What's New in DBOS - September 2026

DBOS for Rust, autoscaling control, faster data retention, new integrations, and more!

!Image 31

Qian Li

Build your reliable backend.

Effortlessly.

Contact sales

Start your project

Get started with DBOS

Use the open source DBOS Transact library, free forever. Pair it with DBOS Pro for premium tooling and support.

Start for free

Explore plans

Products

DBOS TransactDBOS ConductorPricing PlansDBOS vs. Temporal

Customer Stories

Bristol Myers SquibbYutoriDosuAll stories

Developers

DocsQuickstart GuideExamplesTutorialsLLMs.txt

Company

Contact SalesBlogAbout UsOur TeamPartnersCareers

![Image 32: DBOS - Logo](https://www.dbos.dev/)

© 2026 DBOS, Inc.

Privacy PolicyTerms of ServiceOpen Patent PledgeCookies Settings

[](https://discord.com/invite/jsmC6pXGgX)[](https://github.com/dbos-inc)[](https://twitter.com/DBOS_Inc)[](https://www.linkedin.com/company/dbos-inc/)[](https://www.youtube.com/@DBOS-Inc)