The Best Way to Deploy Next.js to

By Manthan Mallikarjun, Published about 1 month ago is an amazing service which allows you to deploy container all across the globe with ease. The have abstracted away a lot of the complexity, to the point where you give them a Dockerfile and they do the rest.

The problem

While Fly works really well for traditional API services, Next.js has a couple of quirks that makes it harder to deploy. Typically your Next.js app will need some public environment variables (prefixed with NEXT_PUBLIC) as well as some private environment variables during the build command.

Fly (rightfully) prefers the official way to provide secrets into a Dockerfile which is more secure than blindly injecting secrets into the build environment. Typically that will look something like this:

Mount the secrets:

RUN --mount=type=secret,id=MY_SUPER_SECRET \
    MY_SUPER_SECRET="$(cat /run/secrets/MY_SUPER_SECRET)" some_command \
    && more_commands_maybe

and then pass it during deployment:

fly deploy \
    --build-secret MY_SUPER_SECRET=some_value


In an application where you have many secrets, this process becomes cumbersome and error-prone. We found a way to make this process easier and more hands-off.

NOTE: does have this CLI solution to make things easier, but it still feels like a cumbersome process

The solution

We have many applications with many secrets. Trying to remember to update the Dockerfile,'s runtime secrets, and GitHub actions was a nightmare. We found a way to make this process easier and more hands-off using a couple of tools.


Our solution involves the following steps:

  1. Store the env vars in a central location. Sync it to and GitHub actions
  2. Build the Next.js application inside of GitHub actions (outside of
  3. Copy the built files and deploy that container to


Infisical is the key to our solution. It allows us to manage our secrets in a single place and then inject them into our Dockerfile, Fly, and GitHub actions. It is open source and free to self host or you can use their hosted solution.

To get started, we set up Infisical, created our project, and added our production secrets. Then we set up the following syncs to and GitHub actions:

Infisical Integrations

Note that for the GitHub integration we specifically use the Repository Environment scope.

GitHub actions

The next step is to build the Next.js application inside of GitHub actions. We use the following GitHub action to build the application:

  runs-on: ubuntu-latest
  environment: production # IMPORTANT, you need this to absorb the synced variables
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
        node-version: lts/*
        cache: 'npm'
    - uses: actions/cache@v4
        path: ${{ github.workspace }}/.next/cache
        key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
        restore-keys: |
          ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
    - run: npm ci
    - uses: koyashiro/[email protected]
        secrets: ${{ toJSON(secrets) }}
    - run: npm run build
    - uses: docker/setup-buildx-action@v3
    - uses: superfly/flyctl-actions/setup-flyctl@master
    - run: flyctl deploy --local-only


Finally, you'll need the Dockerfile to collect the built files and deploy it to First you'll need to make a change in the next.config.js file.

module.exports = {
  output: 'standalone',

This tells Next.js that you'll be running the app in a server as opposed to a serverless environment.

Then you can use the following Dockerfile:

FROM node:lts-alpine as base
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY public* ./public
RUN mkdir .next
RUN chown nextjs:nodejs .next
COPY --chown=nextjs:nodejs .next/standalone ./
COPY --chown=nextjs:nodejs .next/static ./.next/static
USER nextjs
CMD HOSTNAME="" node server.js


Commit and push your changes and watch as GitHub actions build your Next.js application and deploy it to You can now manage your secrets in a single place and have them automatically synced to and GitHub actions.