Deploying with Alchemy
Deploy Better-T-Stack projects to Cloudflare or Prisma and manage hosted databases in the same typed stack
Overview
Alchemy is the infrastructure-as-code layer generated when you choose Cloudflare or Prisma as a deployment target. The CLI writes a packages/infra workspace containing:
- one
alchemy.run.tsfor applications, managed databases, and migrations - exact selected Alchemy and latest Effect beta versions
- root
dev,deploy, anddestroycommands
The CLI calls the deployment choice Prisma. In the generated TypeScript, that target uses Alchemy's Prisma.Compute resource.
Supported deployment shapes
Cloudflare supports a separate Hono server on the Workers runtime and supported web frameworks. Full-stack backend: self projects deploy as one web application.
Prisma supports:
- web: Next.js, Nuxt, Astro, React Router, TanStack Start, SvelteKit, and Solid
- server: Hono, Express, Fastify, and Elysia on Bun or Node
- mixed stacks, such as Cloudflare web + Prisma server or Prisma web + Cloudflare server
The CLI rejects unsupported framework/runtime combinations before writing files.
Alchemy's automatic framework builds handle Next.js, Nuxt, Astro, and TanStack Start. The generated
stack supplies explicit production artifacts for the remaining frameworks: Solid's Nitro
server, React Router's documented custom Express server, and SvelteKit's official adapter-node
server. TanStack Router is rejected for Prisma deployment because it emits static SPA assets while
Prisma.Compute requires an executable server artifact; the generator does not synthesize a static
file server or use Vite's development-oriented preview command.
Cloudflare web deployment uses Alchemy's released framework resources where they match the
generated stable framework: Website.Nuxt for Nuxt, Website.Astro for Astro, and Website.Vite
for React Router, TanStack Router, TanStack Start, and Solid. Next.js and stable SvelteKit keep
their qualified StaticSite paths until the released first-class resources accept the latest
OpenNext and stable SvelteKit versions.
Prisma with a generic node-postgres connection, including PlanetScale Postgres and Supabase, is
temporarily unavailable with Next.js on Cloudflare until OpenNext preserves pg-cloudflare's
workerd runtime files.
Next.js with Neon or Prisma Postgres remains supported on Cloudflare. The same PlanetScale Postgres Prisma setup remains supported with the other Cloudflare SSR frontends or with Prisma, Docker, or Vercel deployment.
# Full-stack Solid on Prisma with PlanetScale MySQL
bunx create-better-t-stack@latest my-app \
--frontend solid \
--backend self \
--database mysql \
--orm prisma \
--db-setup planetscale \
--web-deploy prisma
# Next.js on Cloudflare and a Hono/Bun server on Prisma
bunx create-better-t-stack@latest my-app \
--frontend next \
--backend hono \
--runtime bun \
--database postgres \
--orm prisma \
--db-setup neon \
--web-deploy cloudflare \
--server-deploy prismaProvider login
Install dependencies, then let Alchemy configure every provider used by the stack:
bun install
cd packages/infra
bunx alchemy login --configureAlchemy stores selected provider profiles under ~/.alchemy. A generated managed-database stack does not run the Neon, PlanetScale, or Prisma CLI separately and does not ask you to copy a hosted DATABASE_URL into the application.
Managed databases
Alchemy manages Neon, PlanetScale, or Prisma Postgres only when the application plane that consumes the database is also deployed through Alchemy:
backend: self: the web deployment owns the database- separate backend: the server deployment owns the database
This distinction matters in mixed stacks. A Prisma web app with a Vercel server, for example, keeps the server database external because the Vercel server is the database consumer.
| Setup | Generated behavior |
|---|---|
| Neon | Creates a Neon project, uses the pooled URL at runtime, and the direct URL for migrations |
| PlanetScale Postgres | Creates a PS_DEV database and a least-privilege runtime role; Prisma gets a short-lived migration role |
| PlanetScale MySQL | Creates a PS_DEV database and a read/write runtime password; Prisma gets a short-lived admin migration password |
| Prisma Postgres | Creates the project, database, and connection and injects its pooled application URL |
| Cloudflare D1 | Creates a native D1 binding and applies the generated migration directory |
PlanetScale can charge for PS_DEV databases. Review clusterSize in
packages/infra/alchemy.run.ts before deploying.
Migration workflow
Database schemas remain source controlled. Generate and review migrations during development:
# Drizzle: generate SQL from the current schema
bun run db:generate
# Prisma: create a migration, then regenerate the client
bun run db:migrate
bun run db:generateManaged Prisma projects include an initial checked-in migration when the scaffold generates Better Auth or Todo models. On deployment, Alchemy runs db:migrate:deploy with a migration credential and memoizes it against the checked-in schema and migration files. Drizzle's Neon and PlanetScale resources apply the checked-in Drizzle migration directory directly; Prisma Postgres uses drizzle-kit migrate through the same deploy-time command resource.
Develop and deploy
Run these commands from the project root:
bun run dev
bun run deploy
bun run destroybun run dev starts Alchemy development alongside the generated applications. Application packages expose dev:bare internally so the root command does not recursively start Alchemy again.
For Nuxt and Astro on Cloudflare, Alchemy owns the deployment build and injects its Cloudflare
integration in memory. Nuxt keeps its ordinary framework build command for local verification;
Astro's Cloudflare scaffold relies on the Alchemy lifecycle because a standalone server build would
need a competing checked-in adapter.
Generated projects pin alchemy@2.0.0-beta.72 with Effect rc.108 exactly. The framework source
package is installed in the web workspace that Alchemy loads it from, so Bun, npm, and pnpm can
resolve compatible graphs without overrides or hoisting assumptions.
Deployments are staged. For an explicit production stage:
cd packages/infra
bunx alchemy deploy --stage productionUse the same stage when destroying it:
bunx alchemy destroy --stage productionEnvironment variables and secrets
The stack loads packages/infra/.env plus the relevant app .env files. Public configuration uses Config.string; secrets use Config.redacted. Alchemy Outputs, such as a deployed server URL or database credential, are passed directly so resource dependency ordering is preserved.
For Cloudflare, generated Cloudflare.InferEnv types flow into packages/env/env.d.ts. Managed database bindings are individual Effect inputs, which keeps each runtime property precisely typed.
Generated infrastructure
A managed Prisma Postgres project is conceptually structured like this:
const project = Prisma.Project("project", {
createDatabase: false,
region: "us-east-1",
});
const database = yield * Prisma.Postgres("database", { project });
const connection = yield * Prisma.Connection("database-connection", { database });
yield *
Command.Exec("database-migrations", {
command: "bun run db:migrate:deploy",
cwd: "../../packages/db",
env: { DATABASE_URL: migrationUrl },
memo: { include: ["prisma/migrations/**", "prisma/schema/**"] },
});The generated file is normal TypeScript. You can add resources or adjust regions and sizes while preserving the root lifecycle commands.
For Nuxt + Prisma on Cloudflare, server-side oRPC calls use Nitro's in-process event.fetch against
the generated /rpc route. This keeps the database client in Nitro's server graph only, avoids a
second Prisma WASM copy in the page graph, and keeps the generated Worker below Cloudflare's 3 MB
compressed free-plan limit.
Version policy
Alchemy v2 is still a beta. Better-T-Stack pins one exact Alchemy version together with the selected
Effect release candidate, currently 4.0.0-rc.108, because open prerelease ranges can select
unintended publications. A pinned set is not called compatible until the CLI starts and the complete
generated-project and live gates pass. Upgrade these packages as one verified set rather than
changing one dependency independently.