# The Stack: what Nicolas Dao builds with, and what he never touches

> What I build with right now, what I'd never touch, and why. An opinionated catalog by Nicolas Dao, founder of Cloudless Labs (Sydney): for each job, the tool he would use, what it costs, when to use it and when not to, and what to avoid. Last reviewed 20 October 2026. Web page: https://cloudlesslabs.com/stack/

## How this brief is organised

This file is the catalog behind https://cloudlesslabs.com/stack/, generated from the same data as the page. It is meant for AI assistants helping someone choose a stack, so the decision logic fits in one fetch.

- **Use cases** lists 21 jobs ("what are you building?"). Each gives Nic's picks in order of preference, what he would avoid, and what to use instead.
- **Tools** gives every one of the 79 tools its verdict, when to use it, when not to, and its pricing. These conditions decide between the picks.
- **Biases** are the reasons behind the defaults. A need that conflicts with a bias is where the picks change.
- A verdict marked "Proposed" is research-backed but not yet confirmed by Nic.
- Prices and facts are as of the review date above. The Stack Weekly (https://cloudlesslabs.com/weekly/) checks the catalog against the news every week.
- Each tool's heading below links its full entry (gotchas with fixes, provisioning, alternatives, sources): https://cloudlesslabs.com/stack/<id>/index.md.
- The recommendations are Nicolas Dao's (Cloudless Labs, Sydney). To have this stack built: nic@cloudlesslabs.com.

## Verdicts

- **My default**: 34 tools
- **Use when it fits**: 21 tools
- **Watching**: 3 tools
- **Skip**: 11 tools
- **Never**: 10 tools

## Biases

I'm an AWS consultant. I migrate on-premises systems to AWS, build secure, scalable serverless apps, integrate AI into existing systems, and coach teams to work with Claude Code and Codex.

None of this comes from reading reviews or comparing feature lists. It comes from two decades building software, the last 15 years of them in the cloud, running these tools in production for clients: the pain points are mine, the gotchas are the ones that bit me or my clients, and the fixes are the ones we shipped. Where I'm only watching something, or a verdict is still a proposal, the page says so.

This page is my opinion, and the biases are on the table. Every recommendation says what it costs, when to use it, when not to, what hurts, and how to fix the gotchas I've hit.

It isn't a static list. Every week I review what changed in the tools I use and the ones challenging them. When something new proves itself in my own tests, it earns a place here, and when a pick slips, its verdict changes. What changes week to week goes in the weekly report.

- **AWS-first**: AWS has a service for almost every enterprise need. It's my default cloud.
- **Not Well-Architected to the letter**: Good ideas, but not one-size-fits-all, and biased towards maximising AWS spend. The client's context wins.
- **Infrastructure as code, or it doesn't exist**: A service's greatness includes whether I can provision it with Pulumi or Terraform. If I can't, it loses points.
- **Microsoft only at 10–100x**: I was a certified .NET developer for a long time, and Microsoft has let me down more times than I can count. It's a black box that puts dollars ahead of transparency and developer experience. Microsoft gets a mention only when it is 10 to 100 times better than the alternative. It rarely clears that bar.
- **Google Cloud is not enterprise-complete**: Fine products, not enough coverage for the enterprises I work with.
- **Cloudflare is the edge**: Very cheap and very good at the edge. I use it next to AWS, not instead of it.

## Use cases

### 1. An API for my app

I need a backend API that reads and writes a database.

Picks, in order of preference:

1. **[Lambda function URLs](https://cloudlesslabs.com/stack/aws-lambda-function-urls/index.md)** (My default): Start here. Free front door, one function per concern.
2. **[Neon (serverless Postgres)](https://cloudlesslabs.com/stack/neon/index.md)** (My default): Postgres over HTTP, no VPC or proxy needed.
3. **[Lambda Web Functions](https://cloudlesslabs.com/stack/aws-lambda-web-functions/index.md)** (Watching): Once it's in your region, move HTTP logic to one Express app with a shared connection pool.
4. **[Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md)** (My default): In front, if users are global or traffic is huge (auth, rate limits, caching).

Don't:

- ~~GraphQL and AppSync~~: GraphQL turns your schema into a second product to maintain, and almost no app benefits from it. Plain HTTP endpoints on function URLs do the same job with nothing extra to keep in sync. Instead: [Lambda function URLs](https://cloudlesslabs.com/stack/aws-lambda-function-urls/index.md).
- ~~API Gateway~~: Useful, just not on day one. A function URL already gives you HTTPS, IAM auth, CORS and streaming with nothing else to configure. API Gateway adds routes, stages, deployments and integrations to keep in sync with your code, and a 29–30 second request timeout by default (a function URL allows 15 minutes). Add it when you need what only it does: per-customer API keys and usage plans, request validation, WAF on the endpoint, or writing straight into SQS. Function URLs use the same event format as HTTP APIs, so adding it later changes no code. Instead: [Lambda function URLs](https://cloudlesslabs.com/stack/aws-lambda-function-urls/index.md).

Worked example: [Lambda, Web Functions or Workers?](https://cloudlesslabs.com/stack/guides/compute-choice/index.md)

### 2. An AI chat or LLM feature

My app needs to call an LLM and stream the answer to users.

Picks, in order of preference:

1. **[Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md)** (My default): The proxy. Hides your key, enforces per-user budgets, streams tokens. Waiting is free.
2. **[OpenRouter](https://cloudlesslabs.com/stack/openrouter/index.md)** (My default): Every model behind one key. Pick by cost per intelligence.
3. **[pgvector](https://cloudlesslabs.com/stack/pgvector/index.md)** (My default): If the answers need your data (RAG).
4. **[Cloudflare AI Gateway](https://cloudlesslabs.com/stack/cloudflare-ai-gateway/index.md)** (Watching (Proposed: research-backed, not yet confirmed by Nic)): Caching, spend caps and fallback, on my shortlist.

Don't:

- ~~AWS Lambda~~: Not as the proxy. Lambda bills for every second the function waits on the model, and a streamed answer takes 10–60 seconds. A Worker bills CPU time only, so waiting is free: $33–167 vs about $0 extra for 1M chats. Keep Lambda for the work behind the proxy. Instead: [Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md).
- ~~Anthropic API (for client systems)~~: Not for high-volume features in client products: cost per intelligence decides there, and OpenAI models through OpenRouter win it. (Claude Code stays my main development tool.) Instead: [OpenRouter](https://cloudlesslabs.com/stack/openrouter/index.md).

Worked example: [The LLM streaming proxy](https://cloudlesslabs.com/stack/guides/llm-proxy/index.md)

### 3. Something live that many people watch at once

Live auctions, collaborative editing, polls, chat rooms, game lobbies.

Picks, in order of preference:

1. **[Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md)** (My default): The door. Checks the login and routes each socket.
2. **[Cloudflare Durable Objects](https://cloudlesslabs.com/stack/cloudflare-durable-objects/index.md)** (Use when it fits): The room. One referee per auction, document or room, holding every WebSocket.
3. **[Neon (serverless Postgres)](https://cloudlesslabs.com/stack/neon/index.md)** (My default): The record. Finished events are reconciled into Postgres.
4. **[Amazon S3](https://cloudlesslabs.com/stack/aws-s3/index.md)** (My default): The archive. Bid logs and history as Parquet for analytics.

Don't:

- ~~API Gateway~~: WebSocket APIs bill every message pushed to every watcher, close connections after 2 hours, and hold no state, so you still need a database to decide who's winning. The auction example costs about $407 a month, vs $15–30 with one Durable Object per auction. Instead: [Cloudflare Durable Objects](https://cloudlesslabs.com/stack/cloudflare-durable-objects/index.md).
- ~~GraphQL and AppSync~~: AppSync Events cost about $310 for the same auction, and bring GraphQL-era tooling. A Durable Object is the room and the referee in one. Instead: [Cloudflare Durable Objects](https://cloudlesslabs.com/stack/cloudflare-durable-objects/index.md).

Worked example: [A live auction on Durable Objects](https://cloudlesslabs.com/stack/guides/live-auction/index.md)

### 4. A traffic spike I can see coming

A flash sale, ticket drop or launch where thousands hit at once.

Picks, in order of preference:

1. **[Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md)** (My default): The shock absorber. Workers start in milliseconds at any traffic, with no ramp-up and no concurrency quota, so 0 to 5,000 requests a second just works. Lambda scales fast but in steps: at most 1,000 new instances per 10 seconds per function, capped by the account default of 1,000, each one cold-starting. Junk is rejected at the edge.
2. **[Cloudflare Durable Objects](https://cloudlesslabs.com/stack/cloudflare-durable-objects/index.md)** (Use when it fits): Exact stock counters. Instant "you got one" or "sold out", no overselling.
3. **[AWS Lambda](https://cloudlesslabs.com/stack/aws-lambda/index.md)** (My default): Calm checkout for the winners only. Behind the Worker and the counters, Lambda sees the 2,000 winners, not 50,000 people in ten seconds.
4. **[Stripe](https://cloudlesslabs.com/stack/stripe/index.md)** (My default): Payment.

Don't:

- ~~Lambda function URLs~~: Not as the front door for the crowd. Function URLs are still my default for normal APIs, and Lambda still does the checkout above. The problem is pointing 50,000 people straight at it: the spike hits Lambda's scaling steps and 1,000-instance default, gets 429s, and every new instance opens a database connection at once. Put the Worker in front and only the winners reach Lambda. Instead: [Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md).

Worked example: [The flash sale](https://cloudlesslabs.com/stack/guides/flash-sale/index.md)

### 5. A website, SPA or PWA

A marketing site or a front-end app.

Picks, in order of preference:

1. **[Cloudflare static hosting](https://cloudlesslabs.com/stack/cloudflare-static-hosting/index.md)** (My default): Deploys from GitHub on every push. Static requests are free.
2. **[Cloudflare R2](https://cloudlesslabs.com/stack/cloudflare-r2/index.md)** (Use when it fits (Proposed: research-backed, not yet confirmed by Nic)): Heavy media and downloads with zero egress fees.
3. **[Astro on Cloudflare Workers](https://cloudlesslabs.com/stack/astro-on-workers/index.md)** (Use when it fits): When some pages must be rendered on request: content sites, docs, catalogs.
4. **[Next.js on Cloudflare Workers (OpenNext)](https://cloudlesslabs.com/stack/nextjs-on-workers/index.md)** (Use when it fits): When it's a Next.js app that needs server rendering, without moving to Vercel.
5. **[Cloudflare Turnstile](https://cloudlesslabs.com/stack/cloudflare-turnstile/index.md)** (My default): Any form: Turnstile keeps the bots out, a Worker checks it and Resend sends the email.

Don't:

- ~~Amazon CloudFront~~: Not for a public site you can host anywhere. You'd assemble S3, origin access, a certificate and cache invalidations to get what Cloudflare gives you from a GitHub push, and pay-as-you-go egress can turn a viral page into a real bill where Cloudflare charges nothing. CloudFront stays right for all-AWS clients and private AWS origins. Instead: [Cloudflare static hosting](https://cloudlesslabs.com/stack/cloudflare-static-hosting/index.md).

### 6. Background jobs and retries

Work that runs after the request, with retries.

Picks, in order of preference:

1. **[Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md)** (My default): The queue. Set visibility timeout to six times the function timeout.
2. **[AWS Lambda](https://cloudlesslabs.com/stack/aws-lambda/index.md)** (My default): Batched consumers with partial batch failures.
3. **[Amazon EventBridge](https://cloudlesslabs.com/stack/aws-eventbridge/index.md)** (My default): Routing and schedules.
4. **[Lambda durable functions](https://cloudlesslabs.com/stack/aws-lambda-durable-functions/index.md)** (Watching (Proposed: research-backed, not yet confirmed by Nic)): Watching, for multi-step jobs with flaky dependencies.

Don't:

- ~~AWS Step Functions~~: A JSON state machine for logic that a few lines of JavaScript express better, and one more thing to deploy and debug. SQS with retries covers simple async work; Lambda durable functions are the candidate for multi-step jobs. Instead: [Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md) or [Lambda durable functions](https://cloudlesslabs.com/stack/aws-lambda-durable-functions/index.md).

### 7. Event-driven plumbing

Moving events between services, buffering work, fanning out and streaming records into the lake.

Picks, in order of preference:

1. **[Amazon EventBridge](https://cloudlesslabs.com/stack/aws-eventbridge/index.md)** (My default): Routing: send each event to whoever cares, by its content. Schedules too.
2. **[Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md)** (My default): Work: buffer, retry, dead-letter, and let Lambda take messages in batches.
3. **[Amazon SNS](https://cloudlesslabs.com/stack/aws-sns/index.md)** (Use when it fits): Fan-out: one event to several queues or functions.
4. **[Amazon Data Firehose](https://cloudlesslabs.com/stack/aws-firehose/index.md)** (My default): Streams into S3: records batched, compressed and written as files for the data lake, with no consumer to write.
5. **[Amazon Kinesis](https://cloudlesslabs.com/stack/aws-kinesis/index.md)** (Use when it fits): Only when several consumers must replay the same ordered stream at high volume.

Don't:

- ~~AWS Step Functions~~: A JSON state machine where a queue and a few lines of JavaScript do the job. Instead: [Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md) or [Lambda durable functions](https://cloudlesslabs.com/stack/aws-lambda-durable-functions/index.md).

### 8. A job that runs for hours

A nightly import, report or migration longer than 15 minutes.

Picks, in order of preference:

1. **[Fargate scheduled tasks](https://cloudlesslabs.com/stack/aws-fargate-scheduled-tasks/index.md)** (Use when it fits): Runs only while the job runs, billed per second. Spot if it can restart.
2. **[Amazon EventBridge](https://cloudlesslabs.com/stack/aws-eventbridge/index.md)** (My default): Scheduler starts the task, with time zones.

Don't:

- ~~Lambda container images~~: Not a workaround for long jobs: a container image still stops at Lambda's 15 minutes, with worse cold starts. If it needs a container and hours, run it on Fargate. Instead: [Fargate scheduled tasks](https://cloudlesslabs.com/stack/aws-fargate-scheduled-tasks/index.md).

### 9. Device and IoT data

Thousands of devices sending readings that end up in reports.

Picks, in order of preference:

1. **[AWS IoT Core](https://cloudlesslabs.com/stack/aws-iot-core/index.md)** (Use when it fits (Proposed: research-backed, not yet confirmed by Nic)): Cheapest AWS front door if devices speak MQTT.
2. **[Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md)** (My default): Buffer, then batch into Lambda.
3. **[Neon (serverless Postgres)](https://cloudlesslabs.com/stack/neon/index.md)** (My default): Postgres wins for ingest-and-report.

Don't:

- ~~API Gateway~~: A per-request HTTP front door for a stream of tiny messages. At 216M messages a month, the REST API alone costs about $756. The whole IoT Core pipeline, processing included, costs about $215–230. Instead: [AWS IoT Core](https://cloudlesslabs.com/stack/aws-iot-core/index.md).
- ~~Amazon SNS~~: SNS invokes Lambda once per message: at device volumes that's millions of tiny invocations. SQS lets Lambda take them in batches. Instead: [Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md).
- ~~Cloudflare Durable Objects~~: Each object only sees its own data, so there's no SQL across devices for reports. Use them only for sending commands back to devices; the readings belong in Postgres. Instead: [Neon (serverless Postgres)](https://cloudlesslabs.com/stack/neon/index.md).

Worked example: [IoT ingestion into Postgres](https://cloudlesslabs.com/stack/guides/iot-ingestion/index.md)

### 10. Semantic search and RAG

Search by meaning, or feed an LLM your documents.

Picks, in order of preference:

1. **[pgvector](https://cloudlesslabs.com/stack/pgvector/index.md)** (My default): When a user is waiting and results depend on your data and permissions.
2. **[Amazon S3 Vectors](https://cloudlesslabs.com/stack/aws-s3-vectors/index.md)** (Use when it fits): When the collection is huge, cold or per client, and 100 ms is fine.

Don't:

- ~~Amazon OpenSearch~~: A cluster to size, patch and pay for around the clock, to do what Postgres full-text search plus pgvector already does next to your data. Only worth it for very large text corpora. Instead: [pgvector](https://cloudlesslabs.com/stack/pgvector/index.md).

Worked example: [pgvector or S3 Vectors?](https://cloudlesslabs.com/stack/guides/vectors/index.md)

### 11. Sending email

Transactional emails and newsletters.

Picks, in order of preference:

1. **[Resend](https://cloudlesslabs.com/stack/resend/index.md)** (My default): Both, on one platform, with an MCP server for agents.
2. **[Amazon SES](https://cloudlesslabs.com/stack/aws-ses/index.md)** (Use when it fits): When data must stay in Australia.

Don't:

- ~~Amazon Pinpoint~~: AWS ends support on 30 October 2026. Don't start anything on it; move campaigns to Resend. Instead: [Resend](https://cloudlesslabs.com/stack/resend/index.md).
- ~~Postmark~~: The best deliverability reputation, but no marketing platform, so you'd run two email vendors. Resend does transactional and marketing in one, with idempotency keys and signed webhooks. Instead: [Resend](https://cloudlesslabs.com/stack/resend/index.md).

### 12. A database

Where the data lives.

Picks, in order of preference:

1. **[Neon (serverless Postgres)](https://cloudlesslabs.com/stack/neon/index.md)** (My default): Default. Branches, scale to zero, HTTP driver.
2. **[Aurora PostgreSQL](https://cloudlesslabs.com/stack/aws-aurora/index.md)** (Use when it fits): When the client must stay all-AWS.

Don't:

- ~~Amazon DynamoDB~~: You design every query up front and pay for it in rewrites when the app changes. Postgres with JSONB gives you documents and SQL in one place, and covers every case I've met. Instead: [Neon (serverless Postgres)](https://cloudlesslabs.com/stack/neon/index.md).
- ~~Cloudflare D1~~: A thin business case and no tooling to migrate thousands of databases. For per-tenant SQLite, use the primitive D1 is built on; for a normal database, use Postgres. Instead: [Cloudflare Durable Objects](https://cloudlesslabs.com/stack/cloudflare-durable-objects/index.md) or [Neon (serverless Postgres)](https://cloudlesslabs.com/stack/neon/index.md).

### 13. A data lake

Analytics over everything the apps produce, cheaply, without a warehouse to run.

Picks, in order of preference:

1. **[Amazon S3](https://cloudlesslabs.com/stack/aws-s3/index.md)** (My default): The lake. Parquet (Iceberg next), partitioned by date and sorted by your second key.
2. **[Amazon Data Firehose](https://cloudlesslabs.com/stack/aws-firehose/index.md)** (My default): The way in. Streams records from apps and devices into S3, batched and compressed.
3. **[S3 + Glue + Athena](https://cloudlesslabs.com/stack/aws-athena-data-lake/index.md)** (Use when it fits): Big scans, ad-hoc questions and reports. Zero code, $5 per TB scanned.
4. **[DuckDB](https://cloudlesslabs.com/stack/duckdb/index.md)** (Use when it fits): App queries, writing Parquet, local dev and tests, on the same files. AWS bought the company behind it in August 2026.
5. **[Aurora PostgreSQL](https://cloudlesslabs.com/stack/aws-aurora/index.md)** (Use when it fits): If the app already runs on Aurora: join live tables with the lake in one SQL query (DuckDB inside).

Worked example: [Athena or DuckDB?](https://cloudlesslabs.com/stack/guides/data-lake/index.md)

### 14. Provisioning everything

Infrastructure as code across AWS and every vendor.

Picks, in order of preference:

1. **[Pulumi](https://cloudlesslabs.com/stack/pulumi/index.md)** (My default): Real code, S3 backend, and any Terraform provider.

Don't:

- ~~AWS CloudFormation~~: Never. YAML templates that only reach AWS. Pulumi does the same job in real code and provisions every other vendor too. Instead: [Pulumi](https://cloudlesslabs.com/stack/pulumi/index.md).

Worked example: [Pulumi everywhere](https://cloudlesslabs.com/stack/guides/pulumi-everywhere/index.md)

### 15. Private access and exposing a box

Reach private AWS resources, or publish a service from a server.

Picks, in order of preference:

1. **[Tailscale](https://cloudlesslabs.com/stack/tailscale/index.md)** (My default): Your machines into the VPC.
2. **[Cloudflare Tunnel (cloudflared)](https://cloudlesslabs.com/stack/cloudflare-tunnel/index.md)** (My default): A private service onto a public domain, no open ports.

### 16. Logs without a shocking bill

Observability that doesn't cost more than the app.

Picks, in order of preference:

1. **[Amazon CloudWatch](https://cloudlesslabs.com/stack/aws-cloudwatch/index.md)** (My default): The critical lines: WARN and ERROR, alarms, metric filters, Live Tail. JSON log format, retention set, no payloads.
2. **[Amazon Data Firehose](https://cloudlesslabs.com/stack/aws-firehose/index.md)** (My default): The chatty bulk, side by side: your logger batches DEBUG/INFO into Firehose, which writes to S3. About $75 per TB instead of about $673.
3. **[DuckDB](https://cloudlesslabs.com/stack/duckdb/index.md)** (Use when it fits): Search the S3 logs from a laptop or a Lambda when someone asks what happened.
4. **[Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md)** (My default): Workers Logs moves to $0.25/GB on 1 December 2026.

Worked example: [CloudWatch and Firehose, side by side](https://cloudlesslabs.com/stack/guides/chatty-logs/index.md)

### 17. An AWS foundation for a client

Accounts, access, backups and cost from day one.

Picks, in order of preference:

1. **[AWS Organizations + IAM Identity Center](https://cloudlesslabs.com/stack/aws-organizations-identity-center/index.md)** (My default): Accounts per environment, SSO for people.
2. **[Pulumi](https://cloudlesslabs.com/stack/pulumi/index.md)** (My default): Everything as code.
3. **[AWS Backup](https://cloudlesslabs.com/stack/aws-backup/index.md)** (My default): Backup policies as code.
4. **[Savings Plans and Reserved Instances](https://cloudlesslabs.com/stack/aws-savings-plans/index.md)** (My default): Commit once the baseline is known.
5. **[Cost Explorer (with Claude Code)](https://cloudlesslabs.com/stack/aws-cost-explorer/index.md)** (My default): Monthly review, with an agent doing the digging.

### 18. Moving on-premises systems to AWS

Lift databases and apps into AWS.

Picks, in order of preference:

1. **[Claude Code](https://cloudlesslabs.com/stack/claude-code/index.md)** (My default): First step. It reverse-engineers the existing system: reads the source code, drives the CLIs, maps what's there, then writes the gap analysis and the migration plan, lift-and-shift or modernisation.
2. **[AWS Database Migration Service](https://cloudlesslabs.com/stack/aws-dms/index.md)** (My default): Database migration with replication until cut-over.
3. **[AWS Organizations + IAM Identity Center](https://cloudlesslabs.com/stack/aws-organizations-identity-center/index.md)** (My default): The landing spot.
4. **[Pulumi](https://cloudlesslabs.com/stack/pulumi/index.md)** (My default): The target infrastructure, as code.

### 19. Building with AI agents

How I write and test code now.

Picks, in order of preference:

1. **[Claude Code](https://cloudlesslabs.com/stack/claude-code/index.md)** (My default): Writes the code.
2. **[OpenAI Codex](https://cloudlesslabs.com/stack/openai-codex/index.md)** (My default): Clicks through the app and drives Playwright.
3. **[Playwright, Vitest, pytest](https://cloudlesslabs.com/stack/testing/index.md)** (My default): Agents need tests to know when they're done.
4. **[Stryker mutation testing](https://cloudlesslabs.com/stack/stryker/index.md)** (My default): Proves the tests catch bugs.
5. **[GitHub](https://cloudlesslabs.com/stack/github/index.md)** (My default): Where it all lives.
6. **[Self-hosted MCP servers](https://cloudlesslabs.com/stack/mcp-servers/index.md)** (Use when it fits): Scoped access to client systems.

Don't:

- ~~GitLab, Bitbucket and Atlassian~~: Mediocre tools that teams tolerate out of habit. Agents, CI and every integration land on GitHub first. Instead: [GitHub](https://cloudlesslabs.com/stack/github/index.md).

### 20. Sign-in and identity

How users sign in, reset passwords and use Google, Microsoft or their company login.

Picks, in order of preference:

1. **[Better Auth](https://cloudlesslabs.com/stack/better-auth/index.md)** (My default): First pick for a serious app on Postgres: users in your own database, one store, more features than Cognito, no per-user fee. You build the screens and wire the emails.
2. **[Amazon Cognito](https://cloudlesslabs.com/stack/aws-cognito/index.md)** (My default): The easy pick for small internal apps and admin panels: hosted login, reset emails and social sign-in out of the box, free under 10,000 users.
3. **[Resend](https://cloudlesslabs.com/stack/resend/index.md)** (My default): Sends Better Auth's reset, verification and magic-link emails.
4. **[Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md)** (My default): Verifies the tokens at the edge, from either one.

Worked example: [Better Auth or Cognito?](https://cloudlesslabs.com/stack/guides/sign-in/index.md)

### 21. Running the business

Payments, analytics and the daily tools.

Picks, in order of preference:

1. **[Stripe](https://cloudlesslabs.com/stack/stripe/index.md)** (My default): Payments.
2. **[PostHog](https://cloudlesslabs.com/stack/posthog/index.md)** (My default): Analytics.
3. **[GitHub](https://cloudlesslabs.com/stack/github/index.md)** (My default): Notes and docs as Markdown.

Don't:

- ~~Slack~~: A distraction: always-on chat fragments the day. Email and WhatsApp are enough for clients, and notes and docs live as Markdown in GitHub. Instead: [GitHub](https://cloudlesslabs.com/stack/github/index.md).
- ~~Jira, Asana, Monday.com and heavy project-management software~~: Process for the sake of process. Jira, Asana and Monday.com turn a small team into ticket administrators. A minimal board is enough. Instead: [Trello](https://cloudlesslabs.com/stack/trello/index.md).

## Tools, by layer

### Edge

Between your users and your backend.

#### [Cloudflare Turnstile](https://cloudlesslabs.com/stack/cloudflare-turnstile/index.md)

- Verdict: My default
- Vendor: Cloudflare. Job: Anti-bot for forms
- In one line: Anti-bot for every form: a CAPTCHA replacement most visitors never notice, free and unlimited. With a Worker and Resend, it's a contact form with no form service at all.
- Use it when: Any public form: contact, sign-up, sign-in, password reset, comments. / A static site's contact form: the form posts to a Worker, which checks the Turnstile token, then emails the submission through Resend. About 40 lines, reused for every client, and the submissions never pass through a third party. / Replacing reCAPTCHA: no image puzzles, and no Google tracking your visitors.
- Don't use it when: Nobody can write the 40 lines: a hosted form backend such as Basin or Forminit takes the form and supports Turnstile, but every submission then sits with a third party, on a small free tier.
- Pricing: Free: unlimited challenges, up to 20 widgets per account, 10 hostnames per widget. Enterprise adds unlimited widgets and removes the branding.

#### [Cloudflare Workers](https://cloudlesslabs.com/stack/cloudflare-workers/index.md)

- Verdict: My default
- Vendor: Cloudflare. Job: Edge primitive
- In one line: The edge primitive. It sits between your users and your real backend, and it's very, very good at that.
- Use it when: LLM streaming proxy: hide your API key, check the user's budget, stream tokens back. Waiting on the model costs nothing. / Sudden spikes: no ramp-up limit and no requests-per-second quota. Absorb a flash sale at the edge and pass only real orders to AWS. / Edge front door: JWT checks, rate limits, bot and geo rules, redirects, A/B routing in front of an AWS origin. / Webhook receivers (Stripe, GitHub) that must answer instantly, even if hit three times a month. / Huge streamed downloads and uploads: no response size limit. / The WebSocket entry point for Durable Objects. / Small glue that must run untouched for years: old compatibility dates are supported forever, so there are no forced runtime upgrades.
- Don't use it when: Business logic that needs your private Postgres, AWS events or IAM. Keep that on Lambda. / Native modules (sharp, Chromium, Prisma's engine), child_process, worker_threads, or more than 128 MB of memory. / Strict Australian data residency: Workers run wherever the request lands. / Enterprise clients who need an SLA: the $5 plan has none.
- Pricing: $5 a month per account, including 10M requests and 30M CPU-ms. Then $0.30 per million requests and $0.02 per million CPU-ms. Billed on CPU time only: waiting is free. No egress charges.

#### [Astro on Cloudflare Workers](https://cloudlesslabs.com/stack/astro-on-workers/index.md)

- Verdict: Use when it fits
- Vendor: Astro (open source, MIT), part of Cloudflare since January 2026. Job: Server-rendered content sites
- In one line: The framework I'd pick for content sites that need server rendering. Static by default, server-rendered where a page needs it, and owned by Cloudflare, so Workers is its home.
- Use it when: Content sites (marketing, docs, blogs, catalogs) where most pages are static and a few must be rendered on request. / A new site where you aren't tied to Next.js: Astro runs natively on Workers, with no translation layer. / Fast pages by default: Astro ships no JavaScript unless a component needs it.
- Don't use it when: A highly interactive app (a dashboard, an editor): a React SPA or Next.js fits better. / Your team already knows Next.js well and the site needs per-request rendering: Next.js on Workers keeps them on familiar ground.
- Pricing: Free (MIT). You pay Workers pricing for the pages rendered on request; static pages are free on Cloudflare.

#### [Cloudflare Durable Objects](https://cloudlesslabs.com/stack/cloudflare-durable-objects/index.md)

- Verdict: Use when it fits
- Vendor: Cloudflare. Job: Live shared state at the edge
- In one line: A tiny server with its own SQLite database, addressed by name. One referee for everyone connected to the same thing. Unbeatable for live, shared, short-lived state.
- Use it when: All four together: one thing with live shared state (a lot, a room, a document, a game, a device), many clients connected to it at once, a single referee (first wins, no double-booking), and a lifespan or independence (it lives for an event; you rarely query across many things). / Live auctions and bidding. / Flash-sale and ticket inventory holds (shard by section: about 1,000 requests a second per object). / Collaborative editing and whiteboards (Liveblocks and tldraw run on them). / Live polls, Q&A, chat rooms, game lobbies. / Per-user AI agent sessions (Cloudflare's Agents SDK is built on them), global rate limiters, IoT command and acknowledgement.
- Don't use it when: Ingest-and-report workloads (telemetry, logs, analytics): Postgres wins. / Anything that needs SQL across all objects. There's no cross-object query. / Strict Australian data residency: there's no Australian jurisdiction, only an Oceania location hint. / Per-tenant databases when you need daily cross-tenant reporting.
- Pricing: On Workers Paid: 1M requests included, then $0.15 per million. Duration: 400,000 GB-s included, then $12.50 per million GB-s (always billed at 128 MB, not billed while hibernating). Incoming WebSocket messages count 20:1; outgoing messages are free. Rows written: 50M included, then $1 per million. Storage: 5 GB included, then $0.20/GB-month.

#### [Next.js on Cloudflare Workers (OpenNext)](https://cloudlesslabs.com/stack/nextjs-on-workers/index.md)

- Verdict: Use when it fits
- Vendor: OpenNext (open source), backed by Cloudflare. Job: Server-rendered Next.js
- In one line: Server-rendered Next.js without Vercel. The OpenNext adapter runs a normal Next.js build on Workers: SSR, ISR, server actions, middleware and image optimisation.
- Warning: Cloudflare now points new Next.js apps at vinext, its own rebuild of Next.js on Vite. vinext calls itself experimental: for production, I'd stay on OpenNext until that changes.
- Use it when: A Next.js app that must render pages per request or per user: dashboards, personalised pages, logged-in areas. / Lots of pages that change one at a time (a shop's products, a CMS): ISR rebuilds only the page that changed. / You want Next.js on the stack you already run, not on another platform.
- Don't use it when: A static site or SPA: Cloudflare static hosting is simpler and free. / A content site you're starting fresh: Astro on Workers fits it better. / You need every new Next.js feature on day one: Vercel builds Next.js, and the adapter follows. Node middleware (Next.js 15.2 and later) isn't supported yet. / A heavy app: a Worker is capped at 10 MB compressed on the paid plan (3 MB free), so a big bundle needs trimming.
- Pricing: Workers pricing: Workers Paid is $5 a month with 10M requests and 30M CPU milliseconds included. ISR pages cache in R2.

### Compute

Where business logic runs.

#### [AWS Lambda](https://cloudlesslabs.com/stack/aws-lambda/index.md)

- Verdict: My default
- Vendor: AWS. Job: Events and heavy compute
- In one line: My default for event-driven work and heavy compute. Classic Lambda, zip-packaged, Node.js.
- Use it when: The trigger is an AWS event (SQS, SNS, EventBridge, S3, Cognito, DynamoDB or Kinesis streams, scheduled jobs). / You need memory or CPU per request (up to 10 GB, native modules, Chromium PDFs, sharp, ffmpeg). / Data must stay in an AWS region, including Sydney. / You need private VPC resources or IAM-native calls to other AWS services. / Low-traffic functions the free tier covers entirely.
- Don't use it when: The work is mostly waiting on something else (LLM streaming, slow APIs). You pay for the wait. Use Workers or Lambda Web Functions. / Sudden spikes from zero to tens of thousands of requests a second: Lambda adds at most 1,000 instances per 10 seconds per function, capped by the account default of 1,000, and each new one cold-starts. Put Workers in front. / WebSockets and real-time fan-out. Use Workers with Durable Objects.
- Pricing: $0.20 per million requests + $0.0000133334/GB-s on Arm ($0.0000166667 on x86). Free tier: 1M requests and 400,000 GB-s a month, always.

#### [Fargate scheduled tasks](https://cloudlesslabs.com/stack/aws-fargate-scheduled-tasks/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: Long-running jobs
- In one line: Containers that run only while the job runs. The answer for a two-hour nightly job.
- Use it when: A job runs longer than Lambda's 15 minutes (nightly imports, reports, migrations, batch AI). / A job needs more memory, disk or tooling than a Lambda comfortably has.
- Don't use it when: The job can be broken into small steps that each finish well inside 15 minutes. Lambda is simpler. / The service must answer HTTP 24/7. That's a different (always-on) Fargate design.
- Pricing: Per second of vCPU and memory while the task runs (one-minute minimum). Nothing between runs. Fargate Spot is cheaper if the job can be interrupted.

#### [Lambda Web Functions](https://cloudlesslabs.com/stack/aws-lambda-web-functions/index.md)

- Verdict: Watching
- Vendor: AWS. Job: HTTP business logic, once in your region
- In one line: A real Node.js web server on Lambda, billed on CPU used, 64 to 128 requests per environment. The future of HTTP business logic on AWS. Not in Sydney yet.
- Warning: Not available in all regions. Launch regions: us-east-1, us-east-2, us-west-2, ap-northeast-1 (Tokyo) and eu-west-1 (Ireland). Not in Sydney.
- Use it when: HTTP APIs and server-rendered apps that mostly wait on Postgres: merge many small Lambdas into one Express or Hono app. / Database connection pain: one environment shares one connection pool across up to 128 requests, instead of one connection per Lambda instance. This can remove the need for RDS Proxy. / Streaming responses (LLM chat, server-sent events) when you want them on AWS. / You want plain `node server.js` locally and code you can move anywhere. / Slow requests: 2,000 req/s at 1 s each needs about 32 environments here, but 2,000 Lambda instances (over the default quota).
- Don't use it when: Your users or data are in a region it hasn't reached (Sydney, today): every database query would cross an ocean. / Anything event-driven: it's HTTP only (no SQS, S3, EventBridge or Cognito triggers). / WebSockets: not supported (the Upgrade header is stripped). / Heavy CPU per request: one heavy request slows every other request in the same environment. / Flash sales above 10,000 req/s: that's the default per-endpoint cap.
- Pricing: $0.30 per million requests + $0.0000279955 per active vCPU-second + $0.0000037065 per GB-second of memory consumed while the environment lives (Sydney price-list rates; not launched there yet). Free tier: not stated.

#### [Lambda container images](https://cloudlesslabs.com/stack/aws-lambda-container-images/index.md)

- Verdict: Never
- Vendor: AWS
- In one line: Worse cold starts, and they break Cognito triggers. I stopped using them.
- Don't use it when: Always, unless you genuinely need a 10 GB image or a runtime Lambda doesn't offer.
- Instead: Zip-packaged Lambda functions, or Fargate when you really need a container.

### APIs, queues and events

How requests and events move.

#### [Amazon Data Firehose](https://cloudlesslabs.com/stack/aws-firehose/index.md)

- Verdict: My default
- Vendor: AWS. Job: Managed delivery of records into S3 (and other sinks)
- In one line: The cheapest way to land a stream of records in S3. No servers, no consumers to write, and about $0.04/GB in Sydney.
- Use it when: Chatty application logs: the function's logger batches DEBUG/INFO lines into Firehose, which writes gzip or Parquet files to S3. About $75 a month per TB vs about $673 on CloudWatch. / Streaming events, clicks, device readings or audit records into the data lake, without writing a consumer. / Anything that should end up as files in S3, batched, compressed and retried for you.
- Don't use it when: Work that needs processing per message, retries per message or a dead-letter queue: that's SQS with Lambda. / Several consumers replaying the same stream: that's Kinesis Data Streams. / Logs you need to watch live or alarm on: keep WARN and ERROR in CloudWatch.
- Pricing: Sydney, Direct PUT: $0.037/GB ingested (first 500 TB), billed per record in 5 KB steps. JSON to Parquet conversion $0.023/GB. Dynamic partitioning $0.025/GB plus per-object fees (avoidable). Then normal S3 storage.

#### [Amazon EventBridge](https://cloudlesslabs.com/stack/aws-eventbridge/index.md)

- Verdict: My default
- Vendor: AWS. Job: Events and schedules
- In one line: The event bus and scheduler I use all the time.
- Use it when: Routing AWS and application events to targets by content. / Schedules with time zones (EventBridge Scheduler), including starting Fargate tasks.

#### [Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md)

- Verdict: My default
- Vendor: AWS. Job: Queues and back-pressure
- In one line: The queue in front of Lambda. Natural back-pressure, nearly free when batched.
- Use it when: Anything that should survive spikes and failures between producers and Lambda. / Batched ingestion (IoT, webhooks) into Postgres.
- Don't use it when: Multi-step jobs where a failure in step 3 shouldn't rerun steps 1 and 2: look at Lambda durable functions.
- Pricing: Sydney: $0.40 per million requests (Standard), $0.50 (FIFO). A request carries up to 10 messages, so batching cuts the bill tenfold: 50M messages ≈ $6 batched, $60 unbatched.

#### [Lambda function URLs](https://cloudlesslabs.com/stack/aws-lambda-function-urls/index.md)

- Verdict: My default
- Vendor: AWS. Job: HTTP front door for Lambda
- In one line: My favourite way to put Lambda on the internet. Every MVP starts here.
- Use it when: You need an HTTPS endpoint for one function, with IAM or app-level auth. / MVPs, internal APIs, webhooks into AWS.
- Don't use it when: You need usage plans, API keys per customer, request validation or WAF directly on the endpoint. That's API Gateway territory (or CloudFront or Workers in front).
- Pricing: Free. You pay only for the Lambda invocation.

#### [Amazon Kinesis](https://cloudlesslabs.com/stack/aws-kinesis/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: High-throughput streaming
- In one line: For genuinely huge streams. It gets expensive fast.
- Use it when: Sustained, very high throughput with multiple consumers replaying the same stream.
- Don't use it when: Ordinary ingestion: SQS with batched Lambda is far cheaper.

#### [Amazon SNS](https://cloudlesslabs.com/stack/aws-sns/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: Pub/sub fan-out
- In one line: Fan-out from one publisher to many subscribers. Not my ingestion path at high volume.
- Use it when: One event must reach several queues, functions or emails.
- Don't use it when: High-volume ingestion straight into Lambda (one invocation per message). Put SQS in the path and batch.

#### [API Gateway](https://cloudlesslabs.com/stack/aws-api-gateway/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: Managed API front door
- In one line: Useful in a lot of cases, just not by default. Start on a function URL and add API Gateway the day you need what only it does.
- Use it when: You sell or expose an API to third parties and need per-customer API keys, usage plans and throttling. / Request validation, WAF or custom-domain base paths on the endpoint itself. / Direct AWS service integrations with no Lambda in between (for example API Gateway straight into SQS).
- Don't use it when: High-volume device ingestion: at 216M messages a month, a REST API alone costs about $756, vs about $215–230 for the whole IoT Core pipeline. Use IoT Core or an HTTP API into SQS. / WebSocket fan-out to many watchers: every server push is a paid message, connections last at most 2 hours, and the account default is 10,000 requests a second. / Getting started. A function URL already gives you HTTPS, IAM auth, CORS and streaming. API Gateway adds routes, stages, deployments and integrations to keep in sync with your code, and a 29–30 second request timeout by default (a function URL allows up to 15 minutes).
- Pricing: Sydney: HTTP APIs $1.29 per million requests, REST APIs $3.50 per million, WebSocket messages $1.30 per million plus $0.325 per million connection-minutes.

#### [AWS IoT Core](https://cloudlesslabs.com/stack/aws-iot-core/index.md)

- Verdict: Use when it fits (Proposed: research-backed, not yet confirmed by Nic)
- Vendor: AWS. Job: Device ingestion
- In one line: The cheapest AWS front door for device data, when the devices speak MQTT.
- Use it when: Devices can use MQTT and X.509 identities.
- Don't use it when: Devices only speak HTTP. Use an HTTP API → SQS → batched Lambda.
- Pricing: In my IoT example (5,000 devices, one message a minute), IoT Core → SQS → batched Lambda → Postgres ≈ $215–230 a month vs about $1,014–1,109 for API Gateway REST → SNS → Lambda.

#### [Lambda durable functions](https://cloudlesslabs.com/stack/aws-lambda-durable-functions/index.md)

- Verdict: Watching (Proposed: research-backed, not yet confirmed by Nic)
- Vendor: AWS. Job: Durable multi-step jobs
- In one line: Per-step retries in plain code, inside Lambda. A candidate fix for hand-tuned SQS retry pain.
- Use it when: A job has several steps calling flaky services, and a failure shouldn't rerun the steps that already succeeded.
- Don't use it when: A single-step job: SQS + Lambda is cheaper and simpler. / An SQS-triggered run that would exceed 15 minutes in total.
- Pricing: Example: 1M runs a month of receive → enrich via a flaky API → write Postgres → notify ≈ $47, vs ~$10 for SQS + one Lambda (where a failure reruns every step) and ~$110 for Step Functions Standard.

#### [Cloudflare Queues](https://cloudlesslabs.com/stack/cloudflare-queues/index.md)

- Verdict: Skip (Proposed: research-backed, not yet confirmed by Nic)
- Vendor: Cloudflare
- In one line: Fine when producer and consumer are both Workers. Inside AWS, SQS wins.
- Use it when: Both ends run on Workers, or you need delays longer than 15 minutes.
- Don't use it when: Anything inside AWS. SQS has FIFO, IAM and batched Lambda consumers.
- Pricing: $0.40 per million operations (each write, read and delete; about 3 per message), billed per message so batching saves nothing. 50M messages ≈ $60 vs about $6 on SQS batched.
- Instead: SQS: FIFO, IAM and batched Lambda consumers, inside AWS where the work is.

#### [AWS Step Functions](https://cloudlesslabs.com/stack/aws-step-functions/index.md)

- Verdict: Never
- Vendor: AWS
- In one line: Over-engineered. I've never seen the need in production.
- Don't use it when: Almost always. Plain code in Lambda (or Lambda durable functions) does the job with less ceremony.
- Pricing: Standard workflows: $25 per million state transitions. 1M four-step runs a month ≈ $110.
- Instead: Plain code. SQS for simple async work; Lambda durable functions for multi-step jobs.

#### [GraphQL and AppSync](https://cloudlesslabs.com/stack/aws-appsync/index.md)

- Verdict: Never
- Vendor: AWS
- In one line: Over-engineering. Schema maintenance isn't worth it; plain web APIs are enough.
- Don't use it when: Always, for the apps I build. For real-time, AppSync Events cost about $310 a month in my auction example vs $15–30 on Durable Objects.
- Instead: Plain HTTP APIs on Lambda function URLs, and Durable Objects for real-time.

### Data

Databases, migrations and the data lake.

#### [AWS Database Migration Service](https://cloudlesslabs.com/stack/aws-dms/index.md)

- Verdict: My default
- Vendor: AWS. Job: Database migrations
- In one line: The only migration tool I use. It works.
- Use it when: Moving on-premises or other-cloud databases into AWS, with ongoing replication until cut-over.

#### [Neon (serverless Postgres)](https://cloudlesslabs.com/stack/neon/index.md)

- Verdict: My default
- Vendor: Neon. Job: Default database
- In one line: My go-to Postgres. Scales to zero, branches like git, and talks HTTP so Lambda needs no VPC or proxy.
- Use it when: New apps on Lambda, Workers or anything else. / Preview environments: a database branch per pull request. / Relational data plus JSONB documents: one Postgres replaces a separate NoSQL store at my clients' scale. / Semantic search with pgvector next to the data.
- Don't use it when: The client must stay inside AWS for compliance, or wants Bedrock Knowledge Bases on SQL: use Aurora.
- Pricing: Launch plan $0.106 per compute-unit hour, Scale $0.222; storage $0.35/GB-month; no minimum. Available in Sydney.

#### [Aurora PostgreSQL](https://cloudlesslabs.com/stack/aws-aurora/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: AWS-native Postgres
- In one line: Peace of mind and scale, at a price. My choice when the client must stay all-AWS.
- Use it when: The client is AWS-only, or needs IAM, VPC-only networking and AWS compliance. / Bedrock Knowledge Bases with SQL access to the same chunks (Aurora is the only Postgres they support). / Joining live tables with the data lake in one SQL query: the aurora_analytics extension (DuckDB inside, since 30 September 2026) reads Parquet and Iceberg in S3 as foreign tables, at no extra charge. Read-only, PostgreSQL 17.11+ or 18.6+.
- Don't use it when: New, small or spiky apps: Neon is cheaper and simpler.
- Pricing: Serverless v2 kept at 0.5 ACU always-on ≈ $73 a month. RDS Proxy adds at least about $26 a month (provisioned) or about $146 (Serverless v2, minimum 8 ACU).

#### [DuckDB](https://cloudlesslabs.com/stack/duckdb/index.md)

- Verdict: Use when it fits
- Vendor: DuckDB Foundation (DuckLabs, now part of AWS). Job: Embedded analytics engine
- In one line: SQL over Parquet that runs anywhere, from my laptop to a Lambda. Next to Athena, not instead of it.
- Use it when: App endpoints that run many small, selective queries over the lake (per-customer dashboards, reports): sub-second when warm, no Athena polling. / Writing and reshaping Parquet: COPY gives full control of sort order, file size and partitions, with no 100-partition limit. / Local development and tests: the same SQL against the same S3 data from a laptop, or against sample files in CI with no AWS at all.
- Don't use it when: Scans of tens of GB to TBs per query: one Lambda reads roughly 50 GB per 15 minutes. Athena fans out for you. / Ad-hoc analysis by people who just want a SQL console: Athena.
- Pricing: Free and MIT-licensed. In Lambda you pay compute and S3 GETs: a query that reads 1 GB costs about $0.002, against $0.005 on Athena, with no 10 MB minimum per query.

#### [S3 + Glue + Athena](https://cloudlesslabs.com/stack/aws-athena-data-lake/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: Data lake
- In one line: The data lake that works. Not amazing. Pair it with DuckDB.
- Use it when: Analytics on large volumes of Parquet in S3, including data exported from Durable Objects and apps. / Ad-hoc questions, scheduled reports and scans of tens of GB to TBs: Athena fans out, queues and scales with zero code.
- Don't use it when: An app endpoint firing many small queries: each costs at least 10 MB, takes seconds and needs polling. Use DuckDB in a Lambda.
- Pricing: Sydney: Athena $5 per TB scanned, 10 MB minimum per query. S3 Standard $0.025/GB-month. Glue Data Catalog free for the first million objects.

#### [Amazon OpenSearch](https://cloudlesslabs.com/stack/aws-opensearch/index.md)

- Verdict: Skip
- Vendor: AWS
- In one line: Overkill for most apps. Postgres full-text plus pgvector covers search.
- Use it when: Very large text corpora with heavy faceting and aggregations.
- Don't use it when: Typical app search. Postgres full-text search plus pgvector does it, with no cluster to run.
- Instead: Postgres full-text search plus pgvector, next to the data you already have.

#### [Cloudflare D1](https://cloudlesslabs.com/stack/cloudflare-d1/index.md)

- Verdict: Skip
- Vendor: Cloudflare
- In one line: The business case is thin, and there's no tooling to migrate thousands of databases. Durable Objects do per-tenant SQLite better.
- Don't use it when: One database per tenant at scale: each Worker needs a binding per database (redeploy to add one), migrations run once per database through a rate-limited API (10,000 databases ≈ 42–83 minutes, and going over locks every API call for 5 minutes), and each database is capped at 10 GB.
- Pricing: $5 a month covers 50M reads, 2M writes and 2 GB.
- Instead: Durable Objects for per-tenant SQLite; Neon for a normal database.

#### [Cloudflare Hyperdrive](https://cloudlesslabs.com/stack/cloudflare-hyperdrive/index.md)

- Verdict: Skip
- Vendor: Cloudflare
- In one line: A Postgres connection pool for Workers only. My business logic isn't on Workers, so I don't need it.
- Use it when: Your REST API runs as a Worker and talks to Postgres over TCP.
- Don't use it when: Your logic is on Lambda: Lambda can't use Hyperdrive.
- Pricing: Included in Workers Paid.
- Instead: Neon's HTTP driver, which works from Workers and Lambda alike.

#### [Amazon DynamoDB](https://cloudlesslabs.com/stack/aws-dynamodb/index.md)

- Verdict: Never
- Vendor: AWS
- In one line: I've never had a use case it fits better than Postgres with JSONB.
- Don't use it when: At my clients' scale, always. A huge, simple key-value workload might justify NoSQL, and even then I'd look beyond DynamoDB.
- Instead: Postgres on Neon, with JSONB for the document-shaped data.

### AI

Models, embeddings and agent access.

#### [OpenRouter](https://cloudlesslabs.com/stack/openrouter/index.md)

- Verdict: My default
- Vendor: OpenRouter. Job: LLM access for client systems
- In one line: One SDK, one key, every model. Usually cheaper than running the same open models elsewhere.
- Use it when: Any AI feature in a client system: switch models without changing code.
- Pricing: Model prices pass through; OpenRouter adds 5.5% on credit purchases. Example: DeepSeek V4 Pro at $0.66 / $1.98 per million tokens vs $1.32 / $3.96 on Cloudflare Workers AI.

#### [pgvector](https://cloudlesslabs.com/stack/pgvector/index.md)

- Verdict: My default
- Vendor: Postgres extension. Job: Vectors in your app's database
- In one line: Embeddings next to the data they describe, for when a user is waiting for the answer.
- Use it when: Product or document search inside an app, a support chatbot over a client's knowledge base, 'similar items', duplicate detection. / Results must be filtered by permissions, client, status or dates with SQL joins and row-level security. / Keyword and semantic search together (hybrid). / Answers in milliseconds (about 3–9 ms). / Up to a few million vectors.
- Don't use it when: Tens of millions of vectors queried rarely: the index must fit in RAM (10M vectors ≈ 77 GB). Use S3 Vectors.
- Pricing: Mostly the storage on a Postgres you already pay for: about $5 a month for 1M vectors on an existing Neon database.

#### [Amazon S3 Vectors](https://cloudlesslabs.com/stack/aws-s3-vectors/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: Vector archive
- In one line: Vectors at S3 prices, for huge, cold or per-client collections where 100 ms is fine.
- Use it when: Searching 10 years of contracts or call transcripts, RAG over a document archive. / Long-term memory for one AI agent per customer. / A separate index per client, almost free while idle. / Tens to hundreds of millions of vectors, queried occasionally, where about 100 ms (under a second cold) is fine. / Bedrock Knowledge Bases with the lowest storage cost.
- Don't use it when: Search on the latency-critical path of a busy user request. / Filters that need SQL joins. It has metadata filters only, and no keyword search.
- Pricing: Sydney storage $0.066/GB-month; about $2.50 a month for 1M vectors, $15–154 for 10M, $76–473 for 100M (vs $2,000–12,000 to hold 100M in Postgres). No servers to size.

#### [Cloudflare Sandbox](https://cloudlesslabs.com/stack/cloudflare-sandbox/index.md)

- Verdict: Use when it fits
- Vendor: Cloudflare. Job: Running untrusted and AI-written code
- In one line: Where an AI product runs code nobody reviewed: an isolated container per session, started and driven from a Worker, billed only for the CPU it actually uses.
- Use it when: Your AI feature writes code and runs it: a data-analysis chat running Python on a user's spreadsheet, an agent that installs packages and runs tests. / Users upload scripts or code you must run safely. / You want sessions an agent can come back to: persistent code interpreters, files, a terminal, and backup and restore of the workspace.
- Don't use it when: The code is yours and reviewed: a normal Worker or Lambda is simpler. / Jobs that run for hours on fixed infrastructure: Fargate fits better.
- Pricing: On Workers Paid ($5 a month): active CPU at $0.000020 per vCPU-second, with 375 vCPU-minutes, 25 GiB-hours of memory and 200 GB-hours of disk included each month. Egress from Oceania $0.05/GB after 500 GB.

#### [Self-hosted MCP servers](https://cloudlesslabs.com/stack/mcp-servers/index.md)

- Verdict: Use when it fits
- Vendor: Open standard. Job: Agent integrations
- In one line: Give agents safe, scoped access to client systems. I've started hosting my own.
- Use it when: An agent needs controlled access to a system that has no official MCP server.

#### [Cloudflare AI Gateway](https://cloudlesslabs.com/stack/cloudflare-ai-gateway/index.md)

- Verdict: Watching (Proposed: research-backed, not yet confirmed by Nic)
- Vendor: Cloudflare. Job: LLM traffic control
- In one line: Free caching, rate limits, spend caps and model fallback in front of OpenRouter. Works even from Lambda.
- Use it when: Per-client spend caps and logs for AI features, and automatic fallback when a provider is down.
- Pricing: Core features free. Logs: 20M events a month included on Workers Paid, then $0.60 per million (pricing changes 1 December 2026).

#### [Anthropic API (for client systems)](https://cloudlesslabs.com/stack/anthropic-api/index.md)

- Verdict: Skip
- Vendor: Anthropic
- In one line: Too expensive for production features in client systems. OpenAI models give better cost per intelligence.
- Don't use it when: High-volume features in client systems, where cost per intelligence decides.
- Instead: OpenRouter, picking the model by cost per intelligence.

#### [Cloudflare Workers AI](https://cloudlesslabs.com/stack/cloudflare-workers-ai/index.md)

- Verdict: Skip (Proposed: research-backed, not yet confirmed by Nic)
- Vendor: Cloudflare
- In one line: The same open models cost about twice as much as on OpenRouter, and frontier models are capped at 20 requests a minute.
- Don't use it when: Production features at volume. OpenRouter serves the same open models for about half the price.
- Pricing: $0.011 per 1,000 neurons. DeepSeek V4 Pro: $1.32 / $3.96 per million tokens vs $0.66 / $1.98 on OpenRouter.
- Instead: OpenRouter: the same open models for about half the price, and newer ones.

### Email

Transactional and marketing.

#### [Resend](https://cloudlesslabs.com/stack/resend/index.md)

- Verdict: My default
- Vendor: Resend. Job: Transactional and marketing email
- In one line: My email service. Transactional and marketing on one platform, built for developers and agents.
- Use it when: Any app that sends email: sign-up, receipts, alerts. / Newsletters and product announcements to contact lists, from the same account. / Agents that send or manage email: there's an official MCP server and CLI.
- Don't use it when: Clients whose data must stay in Australia: Resend stores data in the US. Use SES in Sydney.
- Pricing: Transactional: $20 a month for 10k emails, $35 for 100k, $650 for 1M. Marketing: $40 a month for 5k contacts, $180 for 25k, $450 for 100k.

#### [Amazon SES](https://cloudlesslabs.com/stack/aws-ses/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: Email when data must stay in AWS
- In one line: The cheapest email at volume, and the only one that keeps data in Sydney. The sandbox is a pain.
- Use it when: Data residency in an AWS region (Australia included). / Very high volume where price dominates.
- Don't use it when: Data residency isn't a constraint: Resend does transactional and marketing on one platform, with a better developer experience.
- Pricing: New accounts default to the Essentials plan at $0.16 per 1,000 emails (since 21 July 2026); the à la carte rate of $0.10 per 1,000 can still be selected.

#### [Postmark](https://cloudlesslabs.com/stack/postmark/index.md)

- Verdict: Skip
- Vendor: ActiveCampaign
- In one line: The best deliverability reputation, but no marketing platform, no idempotency and unsigned webhooks. I picked Resend.
- Pricing: $15 a month for 10k emails, $115 for 100k, $775 for 1M.
- Instead: Resend: transactional and marketing on one platform, with idempotency keys and signed webhooks.

#### [Amazon Pinpoint](https://cloudlesslabs.com/stack/aws-pinpoint/index.md)

- Verdict: Never
- Vendor: AWS
- In one line: Reaches end of support on 30 October 2026. Don't start anything on it.
- Don't use it when: Always. AWS is ending support.
- Instead: Resend, for transactional and marketing email on one platform.

### Storage and delivery

Files, websites and CDNs.

#### [Amazon S3](https://cloudlesslabs.com/stack/aws-s3/index.md)

- Verdict: My default
- Vendor: AWS. Job: Object storage
- In one line: No complaints. Storage, data lake, archive.
- Use it when: Everything stored and processed inside AWS: uploads, data lakes, backups, logs.
- Don't use it when: Serving large public files to the internet at volume: egress dominates. Use Cloudflare R2 or a CloudFront flat-rate plan.
- Pricing: Sydney: $0.025/GB-month (Standard). Internet egress $0.114/GB (first 10 TB): that's where it hurts.

#### [Cloudflare static hosting](https://cloudlesslabs.com/stack/cloudflare-static-hosting/index.md)

- Verdict: My default
- Vendor: Cloudflare. Job: Websites, SPAs and PWAs
- In one line: Every website I run. Static builds deployed from GitHub on every push to main. Easy, reliable, nearly free.
- Use it when: Marketing sites, single-page apps and progressive web apps.
- Pricing: Static asset requests are free and unlimited.

#### [Amazon CloudFront](https://cloudlesslabs.com/stack/aws-cloudfront/index.md)

- Verdict: Use when it fits
- Vendor: AWS. Job: CDN in front of AWS
- In one line: Fine in front of AWS origins. Egress was too expensive for big public sites, until the flat-rate plans.
- Use it when: An AWS-only client, or origins that must stay private inside AWS.
- Don't use it when: Big, high-traffic public sites on pay-as-you-go pricing: use Cloudflare.
- Pricing: Pay-as-you-go egress is expensive at volume. The flat-rate plans (for example Business: $200 a month for 50 TB) change the argument.

#### [Cloudflare R2](https://cloudlesslabs.com/stack/cloudflare-r2/index.md)

- Verdict: Use when it fits (Proposed: research-backed, not yet confirmed by Nic)
- Vendor: Cloudflare. Job: Public, high-egress storage
- In one line: Zero egress fees. The answer for public files served at volume. Keep data lakes in S3.
- Use it when: Media, downloads and assets served publicly at volume.
- Don't use it when: Data processed inside AWS: reading from private subnets through NAT costs far more than S3. / You need versioning, replication, Object Lock, archive classes or bucket policies. / Australian data residency (no Australian jurisdiction).
- Pricing: $0.015/GB-month, writes $4.50 per million, reads $0.36 per million, internet egress $0. Serving 20 TB a month from 2 TB stored: R2 ≈ $44 vs S3 + CloudFront ≈ $2,158 (or ≈ $228 on CloudFront's Business flat-rate plan).

### Observability

Logs, metrics, alarms.

#### [Amazon CloudWatch](https://cloudlesslabs.com/stack/aws-cloudwatch/index.md)

- Verdict: My default
- Vendor: AWS. Job: Logs, metrics and alarms
- In one line: Good features. Keep it for the logs you watch live and alarm on; send the chatty bulk through Firehose to S3.
- Use it when: WARN and ERROR lines, alarms, metric filters and Live Tail: the logs you need during an incident. / Low-volume functions, where the bill stays small anyway.
- Don't use it when: Chatty DEBUG/INFO logs at volume: about $673 per TB in Sydney vs about $75 through your own Firehose logger. / Lambda's built-in S3 destination as the cheap option: in Sydney it costs $0.335/GB, the same as Infrequent Access.
- Pricing: Sydney: Lambda logs $0.67/GB ingested (the first discount starts at 10 TB a month); Infrequent Access class $0.335/GB; storage $0.033/GB-month. Lambda's built-in delivery to S3 also costs $0.335/GB. Free tier: 5 GB a month.

### Infrastructure as code

If I can't provision it, it loses points.

#### [Pulumi](https://cloudlesslabs.com/stack/pulumi/index.md)

- Verdict: My default
- Vendor: Pulumi. Job: Infrastructure as code
- In one line: Infrastructure as real code. And it can use any Terraform provider, so nearly everything is provisionable from one codebase.
- Use it when: Always.
- Pricing: Free and open source with a self-managed S3 backend (no Pulumi Cloud fees).

#### [GitHub Actions](https://cloudlesslabs.com/stack/github-actions/index.md)

- Verdict: Use when it fits
- Vendor: GitHub. Job: CI/CD
- In one line: The pipeline I'd pick if a project needs one. Today I deploy with Pulumi directly.
- Use it when: A team, not a solo builder, deploys the project.

#### [Terraform / OpenTofu](https://cloudlesslabs.com/stack/terraform/index.md)

- Verdict: Use when it fits
- Vendor: HashiCorp / OpenTofu
- In one line: Fine, and its providers power half of Pulumi's ecosystem. I prefer Pulumi.
- Use it when: The client's team already runs Terraform.

#### [AWS CloudFormation](https://cloudlesslabs.com/stack/aws-cloudformation/index.md)

- Verdict: Never
- Vendor: AWS
- In one line: I hate it. Pulumi does the same job with real code.
- Don't use it when: Always. Use Pulumi.
- Instead: Pulumi: real code, an S3 backend, and every other vendor through Terraform providers.

### Networking and hosting

Private access, exposure, DNS, servers.

#### [Cloudflare Tunnel (cloudflared)](https://cloudlesslabs.com/stack/cloudflare-tunnel/index.md)

- Verdict: My default
- Vendor: Cloudflare. Job: Safe public exposure
- In one line: Expose a service on a private box through a custom domain, without opening a port.
- Use it when: Publishing a service from a VPS or private network, optionally behind Cloudflare Access login.

#### [Hostinger VPS](https://cloudlesslabs.com/stack/hostinger-vps/index.md)

- Verdict: My default
- Vendor: Hostinger. Job: Cheap always-on server
- In one line: Unbeatable price for an always-on box. CPU caps and no serious GPUs.
- Use it when: Agents, scheduled jobs and previews that need a machine running 24/7.
- Don't use it when: GPU workloads or sustained heavy CPU.

#### [Route 53 and Cloudflare DNS](https://cloudlesslabs.com/stack/dns/index.md)

- Verdict: My default
- Vendor: AWS / Cloudflare. Job: DNS
- In one line: Whichever platform hosts the app.
- Use it when: Route 53 for AWS-hosted apps, Cloudflare DNS for anything behind Cloudflare.

#### [Tailscale](https://cloudlesslabs.com/stack/tailscale/index.md)

- Verdict: My default
- Vendor: Tailscale. Job: Private network access
- In one line: Love it. Easy, secure access from my machines to private AWS resources.
- Use it when: Developers and tools need to reach private databases and services in a VPC.

### Accounts, identity and cost

The AWS foundation.

#### [Amazon Cognito](https://cloudlesslabs.com/stack/aws-cognito/index.md)

- Verdict: My default
- Vendor: AWS. Job: Authentication
- In one line: The easiest pick for small apps, internal tools and admin panels. Everything works out of the box and it's free below 10,000 monthly users. For serious apps on Postgres, Better Auth comes first.
- Use it when: Internal apps and admin panels for corporate clients: they stay under the free 10,000 users, and you get the hosted login page, password reset emails and social sign-in with nothing to build. / The app needs AWS-native identity (identity pools, IAM-secured access to AWS services, Amplify, ALB authentication). / Enterprise SSO for lots of users on a tight budget: billed per federated user, not per connection (about $14 a month for 20 connections of 50 users).
- Don't use it when: A serious, growing app on Postgres: users end up in two stores, Cognito and your database, kept in sync with triggers. Better Auth keeps them in one. / You need organisations, magic links, SCIM or self-serve SSO: Cognito has none of them.
- Pricing: Lite and Essentials: 10,000 monthly active users free per account, then $0.015 per user on Essentials (Lite from $0.0055, without passkeys or email codes). Enterprise SSO users: 50 free, then $0.015 each.

#### [AWS Backup](https://cloudlesslabs.com/stack/aws-backup/index.md)

- Verdict: My default
- Vendor: AWS. Job: Backups
- In one line: Central backup policies for AWS resources. Neon branches cover the Neon side.
- Use it when: Any AWS database, volume or file system holding client data.

#### [AWS Organizations + IAM Identity Center](https://cloudlesslabs.com/stack/aws-organizations-identity-center/index.md)

- Verdict: My default
- Vendor: AWS. Job: Accounts and access
- In one line: Centralised accounts, billing and access. Every client gets this first.
- Use it when: Every AWS engagement.
- Pricing: No extra charge.

#### [Better Auth](https://cloudlesslabs.com/stack/better-auth/index.md)

- Verdict: My default
- Vendor: Open source (MIT), part of Vercel since July 2026. Job: Authentication library, users in your Postgres
- In one line: My first pick for serious apps on Postgres. Your users live in your own database, it does more than Cognito, and it costs nothing per user.
- Use it when: A serious app on Postgres that you expect to grow: one user table, in the database you already pay for, joined with everything else. / You need what Cognito doesn't have: organisations and teams, magic links, enterprise SSO without per-user fees, SCIM. / Data must stay in Australia: users sit wherever your Neon or Aurora database is.
- Don't use it when: A small internal app or admin panel that just needs sign-in working today: Cognito does it with less to build. / Nobody will patch it: a library you own is a library you update, weekly. / The client needs an auth vendor's SOC 2 report: a library has none of its own.
- Pricing: Free (MIT), with no per-user fee. You pay only for the Lambda and the Postgres you already run. Optional hosted extras (Better Auth Infrastructure): Pro $20 a month with one self-serve SSO connection, then $50 a month per extra connection.

#### [Cost Explorer (with Claude Code)](https://cloudlesslabs.com/stack/aws-cost-explorer/index.md)

- Verdict: My default
- Vendor: AWS. Job: Cost analysis
- In one line: Where every cost investigation starts. I let Claude Code read it and find the culprit.
- Use it when: Monthly bill reviews and any surprise on the invoice.

#### [Savings Plans and Reserved Instances](https://cloudlesslabs.com/stack/aws-savings-plans/index.md)

- Verdict: My default
- Vendor: AWS. Job: Cost
- In one line: My main lever on AWS cost. AWS margins are high; commitments bring them down.
- Use it when: Any steady baseline of compute or databases.

### Build and test

How the code gets written and checked.

#### [Claude Code](https://cloudlesslabs.com/stack/claude-code/index.md)

- Verdict: My default
- Vendor: Anthropic. Job: AI coding agent
- In one line: My main tool. I haven't hand-written code in two years.
- Use it when: Writing, refactoring, testing and deploying code, including Pulumi stacks. / Reverse-engineering on-premises systems before a migration: it reads the source code, drives the CLIs and maps what's there, then writes the gap analysis and the migration plan, lift-and-shift or modernisation. / Analysing AWS bills: I let it read Cost Explorer and CloudWatch to find what's expensive.

#### [GitHub](https://cloudlesslabs.com/stack/github/index.md)

- Verdict: My default
- Vendor: GitHub. Job: Code hosting
- In one line: Code, notes and docs. Everything integrates with it first.
- Use it when: Always.

#### [Node.js (plain JavaScript)](https://cloudlesslabs.com/stack/nodejs-javascript/index.md)

- Verdict: My default
- Vendor: OpenJS. Job: Language
- In one line: My default language. Best fit for the problem wins, but it's usually this.
- Use it when: Lambda, Workers, Web Functions, Pulumi programs, scripts.
- Don't use it when: The problem clearly needs another language (data science in Python, for example).

#### [OpenAI Codex](https://cloudlesslabs.com/stack/openai-codex/index.md)

- Verdict: My default
- Vendor: OpenAI. Job: Second AI agent
- In one line: For computer control, images, and as an end-to-end test orchestrator.
- Use it when: Computer control, image generation and image understanding, where it does better. / Driving Playwright through complex UI scenarios as an e2e test orchestrator.

#### [Playwright, Vitest, pytest](https://cloudlesslabs.com/stack/testing/index.md)

- Verdict: My default
- Vendor: Open source. Job: Testing
- In one line: End-to-end and unit tests that agents can run and fix on their own.
- Use it when: Every project. Agents need tests to know when they're done.
- Pricing: Free.

#### [Stryker mutation testing](https://cloudlesslabs.com/stack/stryker/index.md)

- Verdict: My default
- Vendor: Open source. Job: Test quality
- In one line: Tests for your tests. It mutates the code and checks your suite notices.
- Use it when: Agent-written code: it proves the tests actually catch bugs.
- Pricing: Free.

#### [TypeScript](https://cloudlesslabs.com/stack/typescript/index.md)

- Verdict: Skip
- Vendor: Microsoft
- In one line: I don't use it. Plain JavaScript and strong tests.
- Instead: Plain JavaScript on Node.js, with strong tests.

#### [GitLab, Bitbucket and Atlassian](https://cloudlesslabs.com/stack/gitlab-bitbucket-atlassian/index.md)

- Verdict: Never
- Vendor: GitLab / Atlassian
- In one line: Mediocre. Never recommended.
- Don't use it when: Always. GitHub does the job better.
- Instead: GitHub, where agents, CI and every integration land first.

### Business tools

Payments, analytics, the daily kit.

#### [PostHog](https://cloudlesslabs.com/stack/posthog/index.md)

- Verdict: My default
- Vendor: PostHog. Job: Analytics
- In one line: Product analytics, feature flags and session replay in one tool.
- Use it when: Understanding how users use the product.

#### [Stripe](https://cloudlesslabs.com/stack/stripe/index.md)

- Verdict: My default
- Vendor: Stripe. Job: Payments
- In one line: Payments, mine and my clients'.
- Use it when: Taking money online.

#### [Dashlane](https://cloudlesslabs.com/stack/dashlane/index.md)

- Verdict: Use when it fits
- Vendor: Dashlane. Job: Password manager
- In one line: Works, but glitchy. Looking for something more robust.

#### [Trello](https://cloudlesslabs.com/stack/trello/index.md)

- Verdict: Use when it fits
- Vendor: Atlassian. Job: Task board
- In one line: Acceptable, if the cards stay minimal.
- Use it when: A client needs to see a simple board.

#### [Jira, Asana, Monday.com and heavy project-management software](https://cloudlesslabs.com/stack/project-management-suites/index.md)

- Verdict: Never
- Vendor: Various
- In one line: A waste of time for a lean team.
- Don't use it when: Always, for lean delivery. A minimal Trello board is enough.
- Instead: A minimal Trello board, and notes as Markdown in GitHub.

#### [Slack](https://cloudlesslabs.com/stack/slack/index.md)

- Verdict: Never
- Vendor: Salesforce
- In one line: A distraction. Email and WhatsApp are enough.
- Don't use it when: Client communication. Email and WhatsApp are enough.
- Instead: Email and WhatsApp for clients; notes and docs as Markdown in GitHub.

### Platforms

The clouds I don't pick.

#### [Google Cloud](https://cloudlesslabs.com/stack/google-cloud/index.md)

- Verdict: Skip
- Vendor: Google
- In one line: Good products, not enterprise-complete enough for my clients.
- Instead: AWS, which covers more of what enterprise clients need.

#### [Netlify](https://cloudlesslabs.com/stack/netlify/index.md)

- Verdict: Skip
- Vendor: Netlify. Job: Web app platform
- In one line: An excellent platform, built around one workflow (push, preview, ship, roll back), and where many AI-built sites already live. I skip it because my clients are enterprises and need far more backend than a web platform covers.
- Use it when: A non-technical team building with AI tools: Bolt and Lovable already publish to Netlify. / You want Claude Code, Codex or Gemini running in the cloud from a prompt or a Linear issue, with each result as a preview link (Agent Runners). / Many collaborators (founders, designers, marketers): one team price, not per seat. / A preview URL for every pull request, each with its own database branch, and one-click rollback, from day one. / Forms on a static site with no backend to write.
- Don't use it when: Most of the work is in the backend: event-driven pipelines, queues, jobs that run for hours, data lakes, IoT. / Enterprise AWS requirements: multiple accounts, IAM, VPCs, or data that must stay inside AWS in Sydney. / Next.js with server rendering that must track every new feature on day one: Vercel does that better. / Costs you need to forecast: credits are hard to predict, and agents that deploy to production often spend them fast.
- Pricing: Pro $20 a month for the whole team, with unlimited members and 3,000 credits. Production deploys, bandwidth, compute and AI usage all draw on credits.
- Instead: Cloudflare static hosting for the front end, Lambda or Cloudflare Workers for the API, Neon for the data, all provisioned with Pulumi.

#### [Vercel](https://cloudlesslabs.com/stack/vercel/index.md)

- Verdict: Skip
- Vendor: Vercel. Job: Web app platform
- In one line: An excellent platform, and its one workflow (push, preview, ship, roll back) is the real product. I skip it because my clients are enterprises: most of their work happens in the backend, and I need all of AWS behind it.
- Use it when: You're building a web app with a small team and nobody wants to run infrastructure: push to GitHub and it's live. / Next.js with server rendering (SSR, ISR, image optimisation, middleware): Vercel builds Next.js, so new features land there first. / You want a preview URL for every pull request, one-click rollback, environment variables and domains on one dashboard, from day one. / An AI product that runs code the model writes (Vercel Sandbox) or long agent workflows (Workflow SDK).
- Don't use it when: Most of the work is in the backend: event-driven pipelines, queues, jobs that run for hours, data lakes, IoT. / Enterprise AWS requirements: multiple accounts, IAM, VPCs, or data that must stay inside AWS in Sydney. / Steady traffic at scale: per-seat pricing plus metered bandwidth and compute cost far more than running the same pieces yourself.
- Pricing: Pro $20 per seat a month, including $20 of usage. Then metered: bandwidth $0.15/GB after the first 1 TB and Active CPU $0.128 an hour (US rates).
- Instead: Cloudflare static hosting for the front end, Lambda or Cloudflare Workers for the API, Neon for the data, all provisioned with Pulumi.

#### [Microsoft Azure, Windows and the Microsoft stack](https://cloudlesslabs.com/stack/microsoft-azure/index.md)

- Verdict: Never
- Vendor: Microsoft
- In one line: Years as a certified .NET developer taught me enough: a black box that puts dollars ahead of transparency and developer experience. Only worth a mention if it's 10–100x better.
- Don't use it when: Always, unless it's 10 to 100 times better than the alternative.
- Instead: AWS, with Cloudflare at the edge.

## Decision guides

- [Lambda, Web Functions or Workers?](https://cloudlesslabs.com/stack/guides/compute-choice/index.md): Where should this piece of logic run? Classic Lambda for events and heavy compute. Lambda Web Functions for HTTP business logic, once in your region. Workers at the edge, for waiting, spikes and real-time.
- [The LLM streaming proxy](https://cloudlesslabs.com/stack/guides/llm-proxy/index.md): How do I add an AI chat feature without leaking my API key or paying for the model's thinking time? Never call an LLM from the browser. A Worker holds the key, checks the user's budget and streams tokens back. Waiting costs nothing on Workers.
- [The flash sale](https://cloudlesslabs.com/stack/guides/flash-sale/index.md): What happens when 50,000 people press "Buy" in the same ten seconds? Straight into Lambda, the spike meets scaling limits, 429s and an Aurora connection storm. With a Worker in front, Cloudflare absorbs it and AWS sees only the winners.
- [A live auction on Durable Objects](https://cloudlesslabs.com/stack/guides/live-auction/index.md): 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.
- [IoT ingestion into Postgres](https://cloudlesslabs.com/stack/guides/iot-ingestion/index.md): 5,000 devices send a reading every minute. Where does the money go? For ingest-and-report, Postgres wins. The expensive part is the front door, not the database. Fix REST and per-message invocations, and the bill drops from about $1,100 to $215–410.
- [pgvector or S3 Vectors?](https://cloudlesslabs.com/stack/guides/vectors/index.md): Where should my embeddings live? Not competitors. pgvector when a user is waiting and results depend on your data. S3 Vectors when the collection is huge, cold or per client.
- [Pulumi everywhere](https://cloudlesslabs.com/stack/guides/pulumi-everywhere/index.md): Can I provision every tool on this page from one codebase? Almost. Pulumi uses any Terraform provider, so AWS, Cloudflare, GitHub, Tailscale, Neon, Resend, PostHog, OpenRouter and Stripe are all code.
- [CloudWatch and Firehose, side by side](https://cloudlesslabs.com/stack/guides/chatty-logs/index.md): How do I keep chatty logs without a CloudWatch-sized bill? Not either-or. WARN and ERROR stay in CloudWatch, where you watch and alarm on them. The chatty bulk goes from your logger to Firehose to S3, at about $75 per TB instead of about $673.
- [Athena or DuckDB?](https://cloudlesslabs.com/stack/guides/data-lake/index.md): How should I query my data lake, and what changed when AWS bought DuckDB's company? Use both. Athena for big scans and ad-hoc questions with zero code. DuckDB for app queries, writing Parquet and local development, on the same files. Sorting files, or Iceberg, fixes the second-index problem without copying data.
- [Better Auth or Cognito?](https://cloudlesslabs.com/stack/guides/sign-in/index.md): Which sign-in should my app use, and what do I actually get for free? Not better or worse: one or the other, by job. Cognito for small internal apps and admin panels: everything out of the box, free under 10,000 users. Better Auth for serious apps on Postgres: users in your own database, more features, no per-user fee, and some building and patching.

## Latest from The Stack Weekly

[Lambda becomes a web server, and AI learns to just decide](https://cloudlesslabs.com/weekly/2026-w43/index.md), 19 October 2026. Every edition: https://cloudlesslabs.com/weekly/index.md

## Related

- [Cloudless Labs: services, clients and contact](https://cloudlesslabs.com/index.md)
- [Map of every Markdown file on cloudlesslabs.com, with what each one answers](https://ask.cloudlesslabs.com)
