Overview
Fjall builds Next.js with OpenNext, which compiles a Next.js app into Lambda bundles plus a folder of static assets. Two entries in Fjall’s pattern registry produce that artefact:How Fjall recognises a Next.js app
fjall apps detect <path> reads each workspace’s package.json and names the
framework from its dependencies. Both dependencies and devDependencies
count.
Detection signals
nextjs+payload is checked first because it is the strict superset. Payload 3
ships as a Next.js plugin, so both dependencies appear in a Payload repository,
and matching nextjs first would misclassify every Payload app.
Two conventions follow from the framework name:
out is a convention, not an observation. Detection never runs the build.
Confirm the directory exists before deploying a static export, or the deploy
succeeds onto an empty bucket.What detection recommends
Detection reports a framework.--pattern takes a pattern. The two vocabularies
are joined by one table, and the verdict says what to do next:
For a recommended pattern, the JSON output also carries the exact
--source,
--build-command and --output-dir values to pass to fjall create app. A
blocked framework carries no prefill, because there is nothing to create.
Choosing the route
Statically exported Next.js
Anext.config.js carrying output: 'export' writes a folder of HTML, CSS and
JavaScript into out/. That folder is exactly what the static-site pattern
serves:
--source is relative to the app’s fjall/web config directory, so ../.. is
the repository root. --output-dir is relative to --source.
Pick the routing mode from what your build writes:
Everything else about that route (clean URLs, security headers, contact forms,
access gates) lives on the static-site pattern page.
Server-rendered Next.js
Server rendering, API routes andnext/image need the OpenNext Lambda shape.
The only construct that builds it is the Payload pattern, and its deploy runs
npx payload generate:importmap before the OpenNext build. It fits a Next.js
app that is a Payload application:
nextjs construct.
--pattern and --tier are honoured only on the non-interactive path. On a
TTY, fjall create app opens the tier wizard, which has no pattern step. Run
bare fjall create and choose Patterns instead, where the picker lists
only the deployable patterns: Payload CMS and Static site.What an OpenNext deploy provisions
Read from the Payload construct, which is the OpenNext pattern that synthesises. The database and the media bucket are Payload’s. The rest is the OpenNext shape any Next.js pattern needs.
All three Lambdas run Node.js 24 on ARM64.
CloudFront routing
Assets are uploaded with a one-year immutable
Cache-Control, which is safe
because Next.js content-hashes their filenames. The distribution defaults to
PriceClass_100.
The build step
Before synthesis, Fjall runsopen-next build, resolving the open-next binary
from the project’s own node_modules rather than from the CLI’s. The build has
a 10-minute ceiling. On failure Fjall deletes .open-next/ so a later synth
cannot pick up a half-written bundle, and a build that exits 0 without writing
.open-next/ is reported as a failure rather than deployed.
After a successful deploy
The deploy ends with the block that brought you here:Redeploy
fjall deploy bare on a
terminal for the menu instead.
When nothing has changed, the run ends with Already up to date — no changes since last deploy. and touches nothing. Pass -f / --force to redeploy every
stack anyway:
--skip-confirmation skips the deployment confirmation prompts and
--auto-approve satisfies the approval gate.
Neither reaches the destruction gate, which is separate and unconditional. A
replacement that loses data, a replacement of a custom-named resource, or a
delete of a stateful resource raises a ticket that only exact, case-sensitive
{physical name, verb} consents clear. --force, --skip-confirmation,
--auto-approve and approval tokens never satisfy it.
Add resources
The pattern is a composition of the same factories you can call directly. Add a queue, a bucket or a second database withfjall add, or edit
fjall/web/infrastructure.ts by hand. See Add Resources.
Point a domain at it
Setdomain on the pattern statement and Fjall creates the ACM certificate and
the Route53 alias record. Full walkthrough, including bring-your-own certificate
and delegated zones: Custom Domain.
Configuration
Pattern configuration lives infjall/<app>/infrastructure.ts, in the second
argument to PatternFactory.build. The root fjall-config.json holds
project-level state (active target, domains, organisation binding) and carries
no pattern configuration.
type, name, domain,
database, compute, storage, messaging, cdn and environment. The
nextjs variant differs in one respect, database being optional rather than
always provisioned, and the deploy reads the pattern’s own database: block to
decide whether a database stack is part of the app. Every option is documented
against the live construct on the
Payload pattern page.
Troubleshooting
fjall create app --pattern nextjs is refused
"nextjs+payload" is a framework, not a pattern
fjall apps detect reports a framework. --pattern takes a pattern. The
error names the mapping:
Cannot find @opennextjs/aws package
node_modules, so a global
install does not satisfy it.
OpenNext build timed out after 600 seconds
The build exceeded its 10-minute ceiling. Run open-next build yourself to see
where the time goes, then trim it. Cold CI machines installing dependencies
inside the build step are the common cause.
OpenNext build completed but .open-next directory not found
The build exited 0 and wrote nothing. Run open-next build in the project and
confirm .open-next/ appears with server-functions/default, assets and
cache inside it.
A static export deploys to an empty site
--output-dir does not match what the build actually wrote. Build first, look
at the directory, then correct the value in fjall/<app>/infrastructure.ts.
Nothing in detection verifies this against disk.
Next steps
Deploy
Every flag on
fjall deploy, plus targets and the approval gateStatic Site Pattern
The route for a Next.js static export
Payload Pattern
Payload on OpenNext, with the full configuration reference
Custom Domain
Certificates, hosted zones and alias records
Add Resources
Extend the app with more AWS services
Create Application
Scaffolding options and tiers
Related documentation
Fjall:- Compute Factory - Lambda configuration
- Storage Factory - S3 bucket options
- CDN Factory - CloudFront behaviours
- Database Factory - Database properties
- Next.js docs - Official Next.js documentation
- Static exports -
output: 'export' - OpenNext - Next.js to AWS Lambda