Guide 04. By Nicolas Dao

A live auction on Durable Objects

How do thousands of bidders share one live auction without races, and what happens when it ends?

One Durable Object per lot holds every WebSocket and processes bids one at a time. When the lot closes, it reconciles into AWS and deletes its data. $15–30 a month vs $130–410 on AWS designs.

The one idea

idFromName("lot-123") reaches the same single instance anywhere in the world. Everyone watching lot 123 connects to that one object, which handles one event at a time. Two bids in the same millisecond are processed in turn; the second sees the first's result.

The protocols

Protocol Role in the auction
WebSockets (hibernating) Every watcher's live connection. Idle objects sleep and aren't billed; outgoing pushes are free.
HTTP Page load: current price, leader, time left.
RPC from a Worker stub.placeBid(user, amount): the REST bid endpoint and admin actions.
Alarms One timer per object at the close time. Anti-sniping just moves it.
SQLite lot and bids tables inside the object.
Outbound fetch Sends the result to AWS.

The flow

 Browsers (500–10,000 per lot)
      │ wss://auctions.example.com/lot/123
      ▼
 Worker ── verify JWT, rate-limit, route by lot ID
      │ idFromName("lot-123")
      ▼
 LotDO "lot-123" (one instance worldwide)
   SQLite: lot + bids · holds every socket · alarm at close time
   │                               │
   │ broadcast each new price      │ alarm fires → lot SOLD
   ▼ (in order, free)              ▼
 every watcher          POST result + full bid log to AWS
                        (retries by alarm until AWS acknowledges)
                                   ▼
          Function URL → SQS → Lambda → Postgres (invoice, Stripe, reports)
                                      └→ S3 Parquet (data lake)
                                   ▼
                        object deletes its own data

The Worker is the door, the object is the room, and AWS gets the minutes when the meeting ends.

The gotcha: await

Single-threaded doesn't mean transactional across an await. If the object awaits an outside call (asking AWS whether the bidder is verified) between reading the price and writing the bid, other bids run in the gap. Do outside checks first; keep read → check → write → broadcast synchronous.

Also: every deploy restarts all objects and drops every socket. Clients reconnect automatically; don't deploy during a live auction.

Cost: 2,000 lots a month, 500 watchers each, 300M pushed updates

Design Monthly
Durable Objects + Worker $15–30
Self-run Fargate + load balancer + Valkey ~$130–165, plus deploys, scaling and failover
AppSync Events + Lambda ~$310
API Gateway WebSocket + Lambda + DynamoDB ~$407 ($390 of it is pushes)

Postgres takes the bids fine with a conditional UPDATE. What's hard on AWS is holding thousands of sockets and pushing every update in order: one hot lot with 10,000 watchers needs 40,000 pushes a second, four times API Gateway's default account limit.

Durable Objects beat everything else when all four are true

  1. One thing with shared live state (a lot, room, document, game, device).
  2. Many clients connected to it at once, receiving pushes.
  3. A single referee: first wins, no double-booking.
  4. A lifespan or independence: it lives for an event, and you rarely query across many things.

Want this stack in your AWS account?

Cloudless Labs is a Sydney consultancy. We migrate on-premises systems to AWS, build serverless apps on this stack, and coach teams to ship with Claude Code and Codex.

Contact Us

For general inquiries, please contact us at:

For e-learning capabilities, please contact: