Lovable apps on TanStack Start
Lovable creates new projects with TanStack Start. Pages render on a server, and server functions run your backend code. Lovable publishes these apps to Cloudflare Workers. ScaleBop builds the same code as a Node.js server and runs it in your AWS account on ECS Fargate behind a load balancer and CloudFront.
Older Lovable projects (React and Vite, without server rendering) still deploy as a static site on S3 and CloudFront. If you upgrade one of them to TanStack Start in Lovable, see When your app changes shape.
What ScaleBop sets up
Section titled “What ScaleBop sets up”You don’t need a Dockerfile. When your repository has none, ScaleBop adds .scalebop-deploy/Dockerfile in its setup pull request. It:
- installs your dependencies with the tool that wrote your lockfile (Lovable uses
bun.lock) - runs your
buildscript withNITRO_PRESET=node-server, so the build writes a Node.js server to.output/instead of a Cloudflare Workers bundle - runs
node .output/server/index.mjson port 3000
Your vite.config.ts and the rest of your code stay as they are, so Lovable keeps working on the same files.
If your repository has its own Dockerfile, ScaleBop builds that instead. See Bring your own Dockerfile.
A server runs all the time, so a TanStack Start app costs more each month than a static site. See Compute and cost. If you upgrade a static Lovable app, the notice that pauses Ship changes shows the monthly estimate before and after.
SPA mode: no server needed
Section titled “SPA mode: no server needed”If your app turns on SPA mode (tanstackStart: { spa: { enabled: true } } in vite.config.ts) and has no server functions (createServerFn) or server routes, nothing needs a server. ScaleBop then deploys it as a static site on S3 and CloudFront, which costs less. CloudFront serves the _shell.html page SPA mode writes for every route.
As soon as the app gains a server function, it needs a server again. ScaleBop notices on the next push and asks you to update the setup.
Environment variables
Section titled “Environment variables”VITE_variables are public: they’re built into the JavaScript your visitors download. Add them under Configuration so they reach the build. The Supabase URL and publishable (anon) key belong here.- Variables without the prefix are server-only. Server functions read them from
process.envwhile the app runs. Add them as secrets under Configuration, for exampleSUPABASE_SERVICE_ROLE_KEY.
Never put a secret behind the VITE_ prefix: anyone who opens your site can read it.
Telling Lovable
Section titled “Telling Lovable”Lovable’s template is written for Cloudflare, so its AI may reach for Cloudflare-only features. The rules under Deployment files → Rules for your AI tool on your project hub include TanStack Start lines that steer it away from them: read secrets with process.env, don’t use Cloudflare bindings, and don’t change the Nitro settings in vite.config.ts. Paste them into your project’s knowledge in Lovable.
If the readiness check stops
Section titled “If the readiness check stops”| Message | What to do |
|---|---|
The app doesn’t build with Nitro, or has no nitro dependency |
Lovable’s current template includes both. Ask Lovable to “upgrade the Vite TanStack config to the latest version and add the nitro package”. |
@lovable.dev/vite-tanstack-config version 1 only builds for Cloudflare Workers |
Upgrade it to version 2 or later, with the same prompt. |
| vite.config pins a Nitro preset | Remove the preset option, so the build target is chosen when the app deploys. |
The app imports Cloudflare Workers modules (cloudflare:*) |
Ask Lovable to “read server secrets from process.env, not Cloudflare bindings”. Those modules only exist on Cloudflare. |
| The Wrangler config declares Cloudflare bindings (KV, D1, R2, …) | Move that data to a service your app reaches over the network, such as Supabase, then remove the bindings. |
Each of these also goes away if you commit a Dockerfile that runs the server.