Chapter 24: When Not to Use Cloudflare
When is Cloudflare the wrong choice?
Every platform has a shape. Previous chapters explored what fits within Cloudflare's shape; this chapter examines what doesn't. Understanding both separates useful guidance from marketing.
Cloudflare works best with native design. When designing a new system where Cloudflare's primitives map to your domain, you'll build faster and run cheaper than anywhere else. But when adapting an existing system or your domain doesn't align with the primitives, you'll spend more effort on workarounds than you might on a different platform.
The shape of the platform
Cloudflare's constraints aren't engineering failures waiting to be fixed. They're the price of the model, trade-offs that enable the benefits.
The 128 MB memory limit exists because thousands of V8 isolates share a single process. Unbounded memory per isolate would destroy the density that makes Workers economical and cold starts instantaneous. The 10 GB database limit exists because D1 databases are Durable Objects, designed to be numerous and cheap, not singular and large. The prohibition on inbound TCP/UDP exists because Cloudflare is fundamentally a proxy network.
The 128 MB memory limit, 10 GB database size, and HTTP-only protocols are fundamental to Cloudflare's architecture, not implementation details that will improve. Waiting for these constraints to "go away" misunderstands the platform. Design within them or choose a different platform.
Some constraints are the price of the model; others are implementation details that will evolve. The memory limit is fundamental. It's the cost of the isolate model. Specific API limitations in Node.js compatibility mode are implementation details that Cloudflare continues to address. Knowing which is which determines whether you should wait, work around, or walk away.
Hard limits and what they mean
Before evaluating architectural fit, check the binary filters: failing any doesn't mean Cloudflare is bad but rather your workload doesn't match the platform's assumptions.
| Constraint | Threshold | If You Exceed It |
|---|---|---|
| Memory per request | 128 MB | Containers (up to 12 GB) or external compute |
| CPU time per request | 5 minutes | Containers, Queues, or external compute |
| Database size (single) | 10 GB | Multiple D1 databases or Hyperdrive to external DB |
| Container resources | 4 vCPU / 12 GB RAM | Hyperscaler compute |
| Inbound protocols | HTTP only (except WebRTC via Realtime) | Traditional cloud with load balancers |
| Cross-partition transactions | None | Saga patterns via Workflows, or external database |
Need more than 12 GB RAM per instance? Need inbound UDP connections (other than WebRTC)? Need true archival storage under $0.001/GB-month? Need cross-database atomic transactions? These aren't "hard but possible"; they're "Cloudflare cannot do this." Check binary filters before investing in detailed evaluation.
Memory: the isolate trade-off
Workers have 128 MB of memory per isolate, shared across concurrent requests, blocking image processing that loads large files, ML inference with substantial models in-process, and any operation building large in-memory data structures.
Workers aren't bad at memory-intensive work; they're simply not designed for it. The isolate model optimises for high concurrency with moderate per-request resources, not resource-intensive individual operations.
When you hit this limit, Containers offer up to 12 GB per instance; stream processing avoids the problem by never loading complete data into memory; external services let you offload specific operations while keeping edge logic in Workers. The question isn't whether 128 MB is "enough" but whether your workload's memory profile matches the isolate model.
CPU time: compute has a clock
Workers have a maximum CPU time of five minutes on paid plans and thirty seconds by default. CPU time means actual compute (cycles spent processing, not wall time spent waiting), a distinction that matters because Workers are designed for I/O-heavy workloads.
The limit blocks video transcoding, complex simulations, and large data transformations; Containers have no CPU time limit within their wall-time constraints, and Queues can help for batch processing (consumers have fifteen minutes of wall time and can spread work across many messages).
But consider whether heavy compute at the edge makes sense. Edge execution's value is latency reduction through geographic proximity, while compute-intensive batch processing benefits from resource availability, not edge proximity. Running heavy compute on Cloudflare to "stay on platform" may optimise for the wrong thing.
Database size: horizontal by design
Each D1 database is limited to 10 GB, preventing single large analytical databases and monolithic legacy migrations, but this "limit" obscures the design intent. With 50,000 databases per account at 10 GB each, you have 500 TB of addressable relational storage; the constraint is per-queryable-unit, not total capacity.
The question is whether horizontal partitioning fits your data model. Multi-tenant SaaS with database-per-tenant works beautifully, as do consumer applications with database-per-user. But a data warehouse aggregating across all tenants or analytical workloads requiring joins across partitions fight the model.
When 10 GB per queryable unit doesn't fit or horizontal partitioning doesn't align with your data model, Hyperdrive provides a production-ready alternative: connecting Workers to existing PostgreSQL or MySQL databases with connection pooling and query caching at the edge. This isn't a compromise or migration stepping-stone but rather a valid permanent choice; many production systems run with Hyperdrive as their data layer. Choose between D1's horizontal model and Hyperdrive's external database connectivity based on your data architecture, not on one being inherently superior.
No inbound TCP/UDP: the proxy architecture
Workers and Containers cannot accept inbound TCP or UDP connections from end users. All traffic flows through Cloudflare's HTTP proxy.
This blocks game servers requiring UDP for latency-sensitive state synchronisation, custom protocol servers, and IoT protocols without HTTP bridges. Cloudflare Spectrum can proxy TCP and UDP traffic, but it's still proxied through their network, not direct to your code.
The exception is Cloudflare Realtime, which handles WebRTC for audio and video communication. If your non-HTTP requirement is specifically real-time media, Chapter 10 covers that capability. But custom protocols, game server UDP, and IoT remain blocked.
This constraint is non-negotiable for general compute. Memory limits can be addressed with Containers. CPU limits can be addressed with different architectures. Protocol requirements (outside WebRTC) cannot.
No distributed transactions
D1 provides transactions within a single database. No transactions span multiple D1 databases, D1 and R2, or D1 and external systems.
The horizontal scaling patterns the platform encourages (database-per-tenant, database-per-user) create exactly the scenario where you might want cross-database transactions. When user A transfers funds to user B and each has their own database, no atomic operation spans both.
The alternative is eventual consistency with saga patterns, implemented through Workflows. Each step persists durably; failures trigger compensating actions. This works, but it's genuinely different from traditional transactions. Intermediate states are visible. Compensating actions have their own failure modes.
Ask yourself: is your domain fundamentally transactional, or have you been using transactions because your database made them easy? If you genuinely need atomic operations across partitions, such as financial systems with regulatory consistency requirements or inventory systems where overselling has legal consequences, the saga pattern may introduce unacceptable complexity.
Red flags: when to look elsewhere
Beyond hard limits, certain project characteristics signal poor platform fit. Consider looking elsewhere if:
"We need to migrate our existing application without changes." Cloudflare rewards native design. Lift-and-shift migrations fight the model at every turn. If you're not willing to rearchitect, hyperscaler VMs or managed containers provide straightforward paths.
"Our data model assumes a single large database." D1's horizontal model deliberately rejects this assumption. However, this doesn't disqualify Cloudflare; Hyperdrive connects Workers to external PostgreSQL or MySQL databases, letting you keep your existing data architecture while gaining edge compute benefits. The question becomes whether you want D1's model or prefer connecting to external databases via Hyperdrive.
"We need atomic transactions across tenant boundaries." Saga patterns provide eventual consistency, but if regulatory or business requirements demand true atomicity across partitions, the complexity cost may exceed any platform benefit.
"Our protocol requires direct UDP connections." Non-negotiable, unless that protocol is WebRTC for audio/video (which Cloudflare Realtime handles).
"Our team has five years of AWS expertise and three months to deliver." Platform fit isn't just technical. If the team is productive on their current platform and the workload doesn't strongly favour edge execution, forcing a platform change may destroy more value than it creates.
"We're already deeply integrated with cloud-native services." A machine learning pipeline using SageMaker, S3, Lambda, and Step Functions has dependencies throughout the AWS ecosystem. Extracting this means replacing not just compute but the integration fabric connecting everything.
If several of these describe your situation, Cloudflare probably isn't your answer. That's not because it's inferior, but because the fit isn't there.
"We need to migrate without changes" combined with "our data model assumes a single large database" combined with "we need atomic transactions across tenants" means Cloudflare doesn't fit. Full stop. Migration means accepting the platform's model or choosing a platform that accepts yours. Forcing incompatible workloads onto Cloudflare wastes engineering time without platform benefits.
Different versus worse
Most platform mismatches aren't limitations but differences: Cloudflare doesn't do X poorly but rather does Y instead. The question is whether X or Y fits your workload.
If you're evaluating Cloudflare after years on AWS, Azure, or GCP, you carry accumulated intuitions about systems that are sometimes universal principles and sometimes adaptations to your current platform's constraints, constraints that may not apply here.
"We need provisioned concurrency for consistent latency" addresses Lambda's cold start problem. Workers' isolate model has sub-millisecond cold starts; the problem doesn't exist. "Connection pooling requires careful tuning" addresses costs that Cloudflare's binding model eliminates. "A single database should scale vertically to any size" reflects traditional RDBMS assumptions that D1 deliberately rejects.
None of these intuitions are wrong in their original context. But applying them uncritically leads to "Cloudflare can't handle our workload" when the accurate conclusion is "Cloudflare handles our workload differently."
The test: can you articulate why the requirement exists independent of your current platform? "We need provisioned concurrency because Lambda cold starts affect latency" identifies a platform-specific adaptation you can drop. "We need 2 GB of RAM because our image processing library loads full images into memory" identifies a genuine resource requirement.
The "almost fits" problem
Clear mismatches are easy (if you need 64 GB of RAM per instance, Cloudflare obviously doesn't fit), but harder cases are partial matches where workloads fit the platform 90% of the time but have components that don't.
An e-commerce platform might handle browsing, sessions, carts, and checkout beautifully on Workers with Durable Objects but require more memory than Workers provide for generating PDF invoices from complex templates. A real-time collaboration tool might handle presence and synchronisation perfectly in Durable Objects but exceed the memory limit when generating document thumbnails.
Three options exist:
Carve out to an external service. PDF generation on a hyperscaler Lambda, image processing on dedicated infrastructure. This adds operational complexity and latency for cross-service calls.
Use Cloudflare Containers. Keep everything on platform but accept Containers' different characteristics: slower startup, fewer locations, different pricing.
Redesign the feature. Perhaps thumbnails can be generated client-side, or PDF generation can be simplified to fit within constraints.
Choosing between them
| Factor | Favour External Service | Favour Containers | Favour Redesign |
|---|---|---|---|
| Latency tolerance | Can accept 50-200ms cross-service overhead | Need <50ms but can accept Container startup | Latency-critical, must stay in Workers |
| Operational appetite | Team comfortable with multi-platform ops | Prefer single platform even with complexity | Prefer simpler architecture overall |
| Feature centrality | Peripheral feature, rarely invoked | Core feature, frequently used | Could work differently without loss |
| Existing solution | Already have this running elsewhere | Starting fresh | Open to alternatives |
The less attractive option is forcing the component to fit through increasingly complex workarounds. If you're building elaborate streaming pipelines to avoid memory limits for conceptually simple operations, consider whether the architecture fits. Either accept the external dependency or redesign the feature.
The lock-in spectrum
Not all Cloudflare usage creates equal platform dependency. Evaluate lock-in across three levels:
Low lock-in: portable by design
R2 is S3-compatible at the API level. Code using AWS SDK against R2 works against S3 with configuration changes. Data exports trivially. Workers running stateless logic with standard JavaScript are similarly portable; platform-specific bindings need replacement, but core logic transfers. KV data exports through straightforward APIs.
Medium lock-in: portable with effort
D1 databases export as SQLite files, standard formats any relational database accepts. But D1 encourages patterns that don't transfer directly. Database-per-tenant with 10,000 separate databases is natural on Cloudflare and awkward elsewhere; you'd likely consolidate during migration.
Queues implement standard patterns but with Cloudflare-specific APIs. The pattern exists everywhere, but you'd rewrite integration code for SQS or Azure Service Bus.
Workflows provide durable execution with step-based guarantees. The concept maps to Step Functions or Temporal, but migration requires understanding both systems deeply enough to translate semantics, not just syntax.
High lock-in: architectural commitment
Durable Objects have no direct equivalent. The combination of single-threaded execution, global uniqueness, automatic placement, and output gating doesn't exist elsewhere. You can implement similar patterns with Orleans, Temporal, or Redis, but you're reimplementing architecture, not porting code.
If Durable Objects handle core coordination in your system, you've made a deep platform commitment. Exit cost isn't data migration; it's architectural redesign.
When to accept each level
Lock-in isn't a problem to avoid but rather a trade-off to make consciously.
Accept high lock-in when the capability enables something competitors can't match. If Durable Objects' coordination primitives let you build real-time features that would be prohibitively complex elsewhere, lock-in is the price of competitive advantage.
Accept medium lock-in when the platform's approach genuinely fits your domain better than alternatives. If database-per-tenant with D1 simplifies your multi-tenant architecture, migration complexity on exit is worth operational simplicity now.
Prefer low lock-in for commodity functionality where platform differences don't create advantage. Object storage is object storage; R2's S3 compatibility gives you Cloudflare's pricing without meaningful commitment.
When hyperscalers are better
Sometimes the right answer is AWS, Azure, or GCP; not because Cloudflare is inferior, but because hyperscalers genuinely fit better.
Deep ecosystem integration
If your architecture deeply integrates cloud-native services, switching cost may exceed any Cloudflare benefit. A machine learning pipeline using SageMaker for training, S3 for data storage, Lambda for preprocessing, and Step Functions for orchestration has dependencies throughout the AWS ecosystem. Each service connects to others with native integrations, shared IAM, and unified tooling.
The same applies to BigQuery-centred analytics stacks, Cosmos DB applications using specific consistency models, and systems built around Azure AD B2C for enterprise identity. These aren't services you happen to use; they're architectural foundations.
Cloudflare can complement these architectures. Workers at the edge can handle caching, authentication, and global routing while backends remain in the hyperscaler. But replacing core services requires justification beyond "Cloudflare is good."
Regulatory and contractual requirements
Some decisions aren't technical. Government contracts may require FedRAMP-authorised infrastructure. Healthcare systems may need specific HIPAA certifications. Financial services may require particular audit capabilities.
Cloudflare has certifications (SOC 2, ISO 27001, GDPR compliance), but may not satisfy every specific requirement. Verify compliance requirements before platform selection, especially for regulated industries or government contracts.
Data sovereignty objections deserve scrutiny, however. If your organisation already uses Cloudflare for security or networking products (CDN, WAF, DDoS protection, Zero Trust access), your data already transits the Cloudflare network on every request. The CDN caches content at edge locations globally; security products inspect traffic in real time.
The Developer Platform stores data at rest, a different category of concern, but the network transit objection has already been resolved by your existing Cloudflare relationship. Organisations sometimes treat Developer Platform adoption as a new data sovereignty decision when it's more accurately an extension of a decision already made. This doesn't eliminate compliance considerations; jurisdictional restrictions on storage remain meaningful. But it reframes the conversation from "should our data touch Cloudflare's network" to "where should data persist within infrastructure we already trust for transit."
For enterprises with strict data residency requirements, the Data Localization Suite provides controls over where TLS keys are stored, where requests are processed, and where metadata persists. These controls come with trade-offs in latency and product compatibility, but they address most regulatory data localisation requirements. See Chapter 21 for detailed coverage of DLS capabilities and constraints.
Heavy GPU compute
Workers AI provides inference at the edge, running models Cloudflare hosts. But Cloudflare offers no general-purpose GPU compute for training models, running custom CUDA workloads, or GPU tasks beyond supported inference. If your workload requires GPUs, hyperscaler instances or specialised ML platforms remain your options.
Specific hyperscaler advantages
| If You Need... | Consider... | Because... |
|---|---|---|
| ML training and custom models | AWS SageMaker, GCP Vertex AI | GPU compute, MLOps tooling |
| Massive analytical queries | BigQuery, Redshift, Synapse | Columnar storage, query optimisation at scale |
| Global multi-master database | Cosmos DB, Spanner | Strong consistency across regions |
| Streaming data pipelines | Kinesis, Pub/Sub, Event Hubs | Native integration with analytics ecosystem |
| Lift-and-shift migration | EC2, GCE, Azure VMs | Minimal application changes |
Hybrid architectures
The choice isn't always binary. Hybrid architectures use Cloudflare for what it does best while keeping workloads elsewhere where that makes sense.
Where to draw the boundary
A reasonable default: put authentication, caching, rate limiting, and request routing at the edge. Put business logic that doesn't benefit from edge proximity in your backend of choice.
Latency sensitivity favours the edge. Operations where round-trip time to a centralised backend degrades user experience (authentication, personalisation, A/B testing, content transformation) benefit from edge execution.
Data locality favours keeping compute near data. If an operation requires substantial data from a centralised database, moving compute to the edge means moving data across the network. Sometimes edge caching addresses this; sometimes the operation genuinely belongs near the data.
Complexity favours simplicity. If an operation requires elaborate workarounds to fit Workers' constraints, complexity cost may exceed latency benefits.
Common hybrid patterns
E-commerce platform: Workers handle product browsing, user sessions, cart management, and checkout flow. Order processing, inventory management, and fulfilment integration run on existing hyperscaler infrastructure with Hyperdrive connections.
SaaS application: Workers handle authentication, tenant routing, and API gateway functions. Core business logic runs on existing infrastructure, with gradual migration of suitable endpoints to Workers as the team builds expertise.
Content platform: Workers handle content delivery, personalisation, and edge caching. Content ingestion, processing pipelines, and search indexing run where data already lives.
The migration gradient
Full platform adoption isn't required for platform benefit. Start with edge concerns that provide immediate value regardless of backend: caching reduces backend load, authentication moves security enforcement forward, rate limiting protects against abuse. These Workers add value alongside any backend without requiring backend changes.
New features can be built natively on Cloudflare while legacy systems remain elsewhere. Migration happens feature by feature, not big-bang. A hybrid steady state, some workloads on Cloudflare and some on hyperscalers, may be the right long-term architecture, not a transitional phase. When you're ready to migrate specific workloads, Chapter 25 provides detailed playbooks for common scenarios.
Exit planning
The best time to plan your exit is when you have no intention of leaving.
What transfers cleanly
Application logic in JavaScript or TypeScript transfers to any JavaScript runtime. Platform-specific bindings need replacement, but logic is portable. D1 exports as standard SQLite or SQL dumps. R2 exports through S3-compatible tooling. Your data isn't trapped.
Architectural knowledge transfers. Teams that understand edge execution, distributed state management, and horizontal scaling apply that knowledge regardless of platform.
What requires reconstruction
Durable Objects' unique model doesn't exist elsewhere. Migration means architectural redesign: implementing actors with Orleans, virtual actors with Azure Durable Entities, or state machines with Temporal. You're designing a new system informed by Durable Objects experience, not porting an existing one.
The coordination patterns enabled by Durable Objects (rate limiting, session management, real-time collaboration) have implementations elsewhere, but they're different implementations. Expect to rebuild rather than migrate.
Reducing exit cost
If exit optionality matters, isolate platform-specific code behind abstractions. A service calling env.DB.prepare() directly throughout is harder to migrate than one where database access flows through an abstraction. This adds development overhead and may not be justified if platform commitment is intentional, but it preserves options.
Document architectural decisions explicitly. Future teams considering migration need to understand not just what the system does but why it was built this way.
Making the decision
Platform selection must be deliberate. Work through these questions sequentially:
The threshold questions
Start with hard limits. If your workload requires more than 128 MB per request routinely, Workers don't fit. If you need more than 4 vCPU or 12 GB RAM per instance, Containers don't fit. If you need more than 10 GB in a single queryable relational store without horizontal partitioning, D1 doesn't fit. If you need inbound TCP or UDP from end users (other than WebRTC for audio/video), Cloudflare doesn't fit.
The fit questions
If hard limits pass, evaluate architectural fit. Does your workload benefit from edge execution? Is latency to end users a meaningful concern? Does horizontal scaling match your data model? Does eventual consistency work for cross-partition operations?
The native design question
Here's the question that cuts through complexity: are you designing for the platform, or adapting despite it?
If Cloudflare's primitives (globally-distributed Workers, single-threaded Durable Objects, horizontally-partitioned D1 databases) map naturally to your domain, the platform accelerates development. If your design requires constant workarounds to fit these primitives, the platform creates friction.
The clearest signal is how you describe your architecture. "We'll use Durable Objects for user sessions because each user's state is naturally isolated" describes native design. "We'll use Durable Objects but we'll need to implement distributed locking across them because our state isn't naturally partitioned" describes adaptation. The first predicts success; the second predicts pain.
Listen to how you describe your architecture. "We'll use X because our domain naturally maps to it" indicates native design. "We'll use X but we need to work around Y constraint" indicates adaptation. Native design accelerates development. Adaptation creates friction you'll fight for months.
The honest assessment
Cloudflare's Developer Platform excels when you're building for it. Global distribution, instant scaling, cost-efficient edge execution, unique coordination primitives; these benefits are real and substantial for workloads that match the model.
The platform doesn't excel when you're adapting existing workloads, fighting resource constraints, or needing capabilities outside the architectural model. Lift-and-shift migrations, memory-intensive processing, GPU compute, and non-HTTP protocols (excluding WebRTC handled by Realtime) find better homes elsewhere.
Cloudflare isn't a general-purpose cloud. It's a specific bet on a specific model: lightweight compute distributed globally, horizontal scaling by default, coordination through Durable Objects rather than distributed transactions. That model solves certain problems better than anything else available and solves other problems poorly or not at all.
Match workload characteristics to platform strengths. Make the bet deliberately. If you're asking "how do I make my workload fit on Cloudflare," you may be asking the wrong question. Either design for the platform or choose a platform that accepts your design.
What comes next
For those who've determined Cloudflare is the right choice, Chapter 25 provides migration playbooks. Moving existing workloads from AWS Lambda, Express.js, or traditional containers requires careful planning; not because migration is technically complex, but because doing it well means migrating incrementally with rollback capability at every step.