Run a NextJS App

Getting an application running on Fly.io is essentially working out how to package it as a deployable image. Once packaged it can be deployed to the Fly.io global application platform.

In this guide we’ll learn how to deploy a NextJS application on Fly.io.

You can deploy your NextJS app on Fly with minimal effort, our CLI will do the heavy lifting. You can use your existing NextJS app or you can create one using the tutorial and then come back here to deploy your app.

Deploy an existing NextJS app


If you just want to see how Fly deployment works, follow these steps.

First, install flyctl, your Fly.io app command center, and sign up to Fly.io if you haven’t already.

Now let’s launch your NextJS app from the root of your application.

cd nextjs-blog
fly launch
Creating app in /Users/me/nextjs-blog
Scanning source code
Detected a Next.js app
? Choose an app name (leave blank to generate one): nextjs-blog
? Select Organization: flyio (flyio)
Some regions require a paid plan (bom, fra, maa).
See https://fly.io/plans to set up a plan.

? Choose a region for deployment: Ashburn, Virginia (US) (iad)
App will use 'iad' region as primary

Created app 'nextjs-blog' in organization 'personal'
Admin URL: https://fly.io/apps/nextjs-blog
Hostname: nextjs-blog.fly.dev
     create  Dockerfile
Wrote config file fly.toml
Validating /Users/rubys/tmp/nextjs-blog/fly.toml
Platform: machines
✓ Configuration is valid

If you need custom packages installed, or have problems with your deployment
build, you may need to edit the Dockerfile for app-specific changes. If you
need help, please post on https://community.fly.io.

Now: run 'fly deploy' to deploy your Next.js app.

That’s it! Run fly apps open to see your deployed app in action.

Try a few other commands:

Generating your NextJS Dockerfile

While you’re welcome to write your own Dockerfile, the easiest way to get started with this is to use the Dockerfile generator. Once installed, it can be run using npx dockerfile for Node applications or bunx dockerfile for Bun applications. You’ll see it referenced throughout this article for various use cases.

Connecting to databases

Unlike some other frameworks, Next.js does not bundle a database adapter. Instead, you are free to choose the ORM or node module you wish.

Fly.io will provide a DATABASE_URL that you can use at runtime to connect to your database. How you will use this will depend on the database module you choose. Prisma is a popular choice, and connecting to your database using Prisma is done through your prisma/schema.prisma file. An example:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

If you are unsure how to connect using your favorite adapter, check out our Vanilla with Candy Sprinkles blog entry and select the configuration that most closely matches your application. If you still have questions, post on our community forum.

Static site generation with databases

Some applications may require database access at build time. By default, the build machine on Fly.io does not have access to your production database. This means you won’t be able to access your database from inside methods like getStaticProps.

Should this be something your application requires, there are two approaches for addressing this: build time secrets and deferring the static site generation until after deployment.

Build time secrets

This approach allows you to inject secrets (such as database URLs) that are only available at build time.

This approach will not work for SQLite3, as the build machine still won’t have access to your volume. However, it can be used with PostgreSQL, MySQL and other such databases.

First, you need to obtain the secrets you will need to deploy. Often this is only the value of DATABASE_URL. If you don’t know the value of this secret, fly console and printenv DATABASE_URL can obtain this value for you.

Next, you need to modify your Dockerfile to mount a secret. The easiest way to do this is using the Dockerfile generator, like so:

npx dockerfile --mount-secret=DATABASE_URL

It is possible to mount multiple secrets, and you can read more about mounting secrets for further details.

Finally, you need to pass the secret on each deploy:

fly deploy --build-secret DATABASE_URL=value

Replace value above with the actual secret. It might be worth putting this command into a shell script or batch file.

Deferring static site generation

An alternate approach, one that works with SQLite3 too, is to defer the running of the build step to just after deployment before your web server is started. The upside is that your build has full access to all of your deployment secrets and environment variables.

This involves replacing the entry point in your Dockerfile (typically your CMD) with a script and having that script run npm build (or equivalent) prior to starting your server.

You can let the Dockerfile generator take care of these changes for you:

npx dockerfile --build=defer

Downsides of this approach:

  • Your deployment machines will need enough memory to run a build. See: fly scale memory and swap_size_mb for two options.
  • You may need to adjust the grace_period for any HTTP service checks.
  • If you are only running one machine there will be a period of time where your server is inaccessible while the site is being statically generated.
  • If you run multiple machines, the static site generation will be run on each increasing the total time before any changes are fully deployed.

Exposing environment variables to the browser

If you’re a NextJS user you might know that it supports exposing environment variables to the browser using variables with names starting with NEXT_PUBLIC_. These variables are fixed at build time, and unlike runtime environment variables (which are only available on the server), they can’t be changed unless the application is built again.

In order to make these NEXT_PUBLIC_ variables available, you can add them as ARG variables in your Dockerfile, as seen in the example below. This should come after any actual build steps (if you’ve used the Dockerfile generator or the Dockerfile created from fly launch, this would be after the line FROM base as build).

# Build arguments
ARG NEXT_PUBLIC_EXAMPLE="value"
ARG NEXT_PUBLIC_OTHER="Other value"

Alternatively, if you’d prefer to use the Dockerfile generator, you can do so by running the following:

npx dockerfile "--arg-build=NEXT_PUBLIC_EXAMPLE:value" \
  "--arg-build=NEXT_PUBLIC_OTHER=Other value"

Disabling telemetry in production

During server startup, the following messages may appear in your log:

Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://nextjs.org/telemetry

The following command can be used to modify your Dockerfile to disable telemetry:

npx dockerfile --env-base=NEXT_TELEMETRY_DISABLED:1

Common pitfalls


Out of memory: Killed process

If you should happen to see lines like the following in your logs:

Out of memory: Killed process

Two links that may be of help:

Fetch failure when optimizing images

If your application uses the <Image> element and your images aren’t showing and you are seeing the following in your logs:

TypeError: fetch failed

Scan backward in your logs for the following:

Warning: For production Image Optimization with Next.js, the optional 'sharp' package is strongly recommended. Run 'npm i sharp', and Next.js will use it automatically for Image Optimization.

Read more: https://nextjs.org/docs/messages/sharp-missing-in-production

Follow the instructions in the message, and then redeploy:

npm i sharp
fly deploy