Back to blog
Engineering

Running an AI App Platform Entirely on Cloudflare

YYuan team2026-07-145 min read

Yuan is a platform where anyone can build an AI Native app from a single sentence. It comes with a less visible engineering fact: the public site, the control plane, the database, object storage, and the sandboxes where AI agents run all live on Cloudflare's developer platform. We don't run Kubernetes or a cluster of our own, and nobody on the team does ops full-time.

This post covers why we chose this stack, what the architecture looks like, where it exceeded expectations, and where it wore us down. To be clear up front: Cloudflare didn't sponsor this post. We're just heavy users.

What AI workloads look like

A year of running an AI product changed how we think about load. Classic web traffic is a large volume of cheap requests. Here, a single user interaction might kick off an agent turn that runs for minutes, or it might just read one row. The swings are also wider: one social media mention can multiply traffic tenfold within minutes, and overnight it drops to almost nothing.

The trait that shaped our design most, though, is that the platform runs untrusted, AI-generated code on users' behalf. A traditional PaaS assumes code was written and reviewed by a developer. An AI Native platform has to assume that any piece of running logic might be a model's improvisation, so isolation has to be built in from the ground up.

The architecture

Cloudflare offers isolation as a primitive at three granularities, and we use all three:

  • Workers (V8 isolates): millisecond cold starts, carrying every request path. The Next.js frontend runs here via OpenNext, and the control plane is a separate Worker.
  • Durable Objects: every sandbox and every app lifecycle gets a single-threaded, stateful coordinator. The "globally unique, strictly serial" property removes an entire class of concurrency bugs at the architecture level.
  • Containers / Sandbox: AI agents run in real containers. They can install anything and execute anything, and when something blows up it only takes down its own room.

The data layer is D1 (SQLite at the edge) and R2 (object storage with zero egress fees). Components talk to each other over Service Bindings and never cross the public internet.

The three decisions we're happiest with

The public face and the privileged face are two separate Workers. The Worker serving public traffic holds no sandbox, database, or storage bindings; every privileged operation crosses a single Service Binding into the control-plane Worker. Even if generated code achieved RCE on the public side, there would be no directly usable credentials or bindings in its hands. That guarantee comes from the topology itself, not from how careful anyone was in code review.

Model keys never enter a container. Agent containers hold no real upstream API keys. When code inside a container calls a model, the request is intercepted at the platform boundary and credentials are injected on the platform side before forwarding, so generated code can dump its entire environment and find nothing usable outside. The interception rides the Sandbox SDK's egress hooks, which saved us from running a proxy fleet of our own.

Apps' server-side logic runs in dynamically loaded child isolates. This logic used to run in containers, with cold starts measured in seconds. After moving it to Worker Loaders (isolates loaded on demand), the cold starts mostly disappeared. An isolated runtime per app is an infrastructure project on most platforms; here it's an API call.

Where it wore us down

In fairness, these belong in the story too.

  • D1 has no interactive transactions. You can't open a transaction, read, think, then write. Every atomic write has to be organized as a single batched commit, and we rewrote our data layer into a "read first, compute, commit once" shape to cope. In hindsight it made write paths easier to reason about. The migration weeks were still not fun.
  • Workers are not Node. A lot of the Next.js ecosystem assumes Node underneath. OpenNext has paved the main road, but late on some build night you will hit a dependency that assumes fs exists. If you pick this stack, budget runtime-compatibility triage as a real cost.
  • Container cold starts are real. Waking a sandbox for the first time pulls an image and boots a process, which takes seconds to tens of seconds. Our answer is layering: anything that can run in an isolate never touches a container, containers are reserved for agent workloads that genuinely need a full Linux, and pre-warming covers the rest.

Looking back

The biggest thing this platform gave us is isolation as an off-the-shelf primitive. Isolates, Durable Objects, and containers each come with one clean API, and together they form the skeleton of a multi-tenant untrusted-code platform without a months-long infrastructure project first. The cost shape helps too: pay-per-use means no idle cluster reserved for peaks, which matters a lot while your user count is still climbing. And one thing that's easy to undervalue: billing, logs, deploys, and secrets live in one system, so a small team spends its attention on the product instead of on glue.

If you're building something that runs AI-generated logic on your users' behalf, this route deserves a serious look. Yuan runs on it end to end, and the Gallery shows what comes out the other side.

Y
Yuan team
Yuan team
More articles