Next.js
Fly.Website.Nextjs deploys a Next.js app to
Fly as a long-running Node process. It runs a real next build, then
a serve entry that import("next") +
next({ dev: false }).prepare() + getRequestHandler() on port
3000:
- one
Fly.App(created ifappis omitted) and aFly.Servicerunning Node on a Machine - a shared IPv4 so
https://{app}.fly.devanswers .nextandpublic/baked into the image;next,react, andreact-dominstalled unbundled
This is not OpenNext — those wrappers target Lambda and workerd.
ISR and next/image use Next’s own Node behavior.
App Router and Pages Router both work — server components, API
routes, middleware, server actions, dynamic segments, streaming SSR,
and getServerSideProps pages all run in the Node process.
Install
Section titled “Install”The build integration is not bundled with alchemy. Install
@alchemy.run/frontend-frameworks; the resource loads the package’s
/nextjs/node export from your project at deploy time. It is only
used at build time, so a dev dependency is enough. Your app still
needs next, react, and react-dom as usual:
bun add -d @alchemy.run/frontend-frameworksnpm install -D @alchemy.run/frontend-frameworkspnpm add -D @alchemy.run/frontend-frameworksyarn add -D @alchemy.run/frontend-frameworksConfigure Next.js
Section titled “Configure Next.js”Your next.config.* is loaded and honored as-is — the build runs a
real next build, and Alchemy never rewrites the file. There is no
adapter to install and no open-next.config.ts to write.
Pass rootDir when the Next.js project is not the stack root.
Declare the Website
Section titled “Declare the Website”Declare the site as a module-level const (rather than inline in the Stack):
import * as Fly from "alchemy/Fly";
export const Website = Fly.Website.Nextjs("Website");Add it to the Stack
Section titled “Add it to the Stack”Yield the site from your Stack and return its URL:
import * as Alchemy from "alchemy";import * as Effect from "effect/Effect";
export default Alchemy.Stack( "MyNextjsSite", { providers: Fly.providers(), state: Alchemy.localState(), }, Effect.gen(function* () { const site = yield* Website; return { url: site.url }; }),);url is https://{app}.fly.dev on deploy. app, service, ip,
and certificate are set then and undefined under alchemy dev.
Pass an existing App to share it with other Services:
export const Website = Fly.Website.Nextjs("Website", { app: Site });Add environment variables
Section titled “Add environment variables”Process environment is the top-level env prop — plain strings, or
Redacted secrets:
export const Website = Fly.Website.Nextjs("Website", { env: { GREETING: "Hello from Alchemy!", API_BASE: "https://api.example.com", },});The values are copied onto process.env before next build /
next dev, and onto the Machine at deploy, so server code and
NEXT_PUBLIC_* inlining see the same map in both modes.
Read the environment in server code
Section titled “Read the environment in server code”The Next server is a plain Node process, so route handlers, server
components, and server actions read the environment from
process.env:
export function GET() { return Response.json({ greeting: process.env.GREETING ?? "hello" });}Local dev
Section titled “Local dev”bun alchemy devalchemy dev runs next dev (native HMR, Turbopack) instead of
deploying; site.url is the local address and no Fly resources are
created. Wrap the site in Alchemy.remote() to deploy the live
Service even during dev.
Custom domain
Section titled “Custom domain”const site = yield* Fly.Website.Nextjs("Web", { domain: "app.example.com",});Alchemy requests an ACME Fly.Certificate on
the App and url becomes https://app.example.com. Point DNS at
the App first — v1 does not create DNS records.
Where next
Section titled “Where next”Fly.Website.Nextjsreference — every prop and attribute- Apps, Services, IPs & certificates