Skip to content

How to Deploy Next.js on Multiple Servers

Dealing with multiple Next.js instances served by a load balancer with no session affinity

Multi-server deployments in Next.js are tricky. Without the right configuration, every time your browser is served with a different instance by the load balancer, a hard refresh will be performed. This happens because your browser will not be able to identify the instances your running application consists of as part of the same application.

This breaks the way Next.js usually retrieves data and does not allow it to work properly. Consequently, not only does this lead to poor performance, but it also prevents the distinctive features of Next.js from working.

In other terms, you may end up losing almost all the benefits coming with Next.js. Fortunately, it can be configured to avoid all this. So, let’s see how to make a Next.js application work as expected when deploying it on a multi-server environment.

Understanding the Next.js Build ID Logic

Next.js uses a constant id generated at build time to identify which version of your application is being served. This can cause problems in multi-server deployments when next build is run on every server. In order to keep a static build id between builds you can provide your own build id. — Configuring the Build ID

As explained in the official documentation, whenever next build is launched, Next.js produces a new build ID uniquely identifying the newly generated instance. This means that the aforementioned multi-server deployment issues are only present when the next build command is run many times. In fact, by launching the build command once and then deploying the resulting build package on different servers, each instance would have the same build ID.

Unfortunately, multi-server environments with a load balancer usually work by launching the build command every time a new instance needs to be created based on traffic. Basically, when the current servers are too loaded, the deployment environment takes care of creating a new instance on the fly. And to do this, next build is launched, leading to the problem of different build IDs.

Keep in mind that having several instances with different build IDs is not inherently a problem. Problems only occur when the same user is served with different instances in the same navigation session. This means that if your multiple instances are served by a load balancer with session affinity, the end-user will not encounter any issues. This is because one user will be served always with the same instance.

On the other hand, the instance that the user is now using may be deleted because of low traffic. In this case, the load balancer will be forced to serve the user with another instance. Thus, you should not rely on the load balancer alone.

Multi-Server Deployments in Next.js

As mentioned here, Next.js allows you to define a custom build ID. Specifically, you can do it by using the generateBuildId property inside your next.config.js file, as follows:

module.exports = {
  generateBuildId: async () => {
    return "my-build-ID"
  },
}

Generating a proper build ID is not a trivial task. For example, avoid using the current timestamp because the next build commands are all launched at different times. A good solution is using the latest git commit hash. This would assure that all instances will have the same build ID.

To achieve this, you can use the next-build-id npm package. First, add it to your project’s dependencies with the following command:

npm install next-build-id

Then, you can use it as follows:

const nextBuildId = require("next-build-id")

module.exports = {
    generateBuildId: () => nextBuildId({ dir: __dirname })
}

The nextBuildId() function will return the latest git commit hash from your local git repository. In other terms, this is equivalent to:

git rev-parse HEAD

The next-build-id also offers other options. Learn more about it on its official GitHub page.

To verify that your Next.js instances are using the right build ID you can look for the BUILD_ID text file stored in your build directories. By default, it is the .next directory. Otherwise, you can verify it from the URL of the static resources served by Next.js. You can retrieve these URLs from the Network section of your browser dev tools. In fact, this is the URL format used by Next.js for static files when setting a build ID:

/_next/static/<build-ID>/<static-file>

For example, the following static resource:

https://www.example.com/_next/static/_buildManifest.js

will become:

https://www.example.com/_next/static/c657j0328d606cc59cffafe62c09926eb6b749de/_buildManifest.js

As you can see, the static resource URL now includes “c657j0328d606cc59cffafe62c09926eb6b749de”, which represents the git commit hash used as build ID.

next-build-id is a great tool, but keep in mind that it only works when you have access to your .git folder. And this is not the case when dealing with AWS Elastic Beanstalk, for example.

So, let’s see how to give build ID a proper value when using AWS Elastic Beanstalk.

Deploying on AWS Elastic Beanstalk

The eb deploy command required by AWS Elastic Beanstalk to perform a deployment relies on the git archive command. This does not copy the .git folder, making next-build-id unable to work.

Fortunately, the flippidippi GitHub user found a workaround. As explained here, you can generate a build ID equal to the latest git commit hash from your local git repository without next-build-id as follows:

First, in next.config.js give the generateBuildId property the following value:

module.exports = {
  generateBuildId: () => {
      if ("$Format:%H$".startsWith("$")) {
        return "static-build-id"
      }
      return "$Format:%H$"
    },
}

Then, create a .gitattributes file in your root directory and initialize it with the following line:

next.config.js export-subst

This command will be used by git archive and will take care of replacing the “$Format:%H$” string with the current git commit hash.

The if statement is required to build the project locally. This is because “$Format:%H$” is not a valid build ID. So, the npm run build command launched locally would fail without it. In particular, when next.config.js export-subst is applied, the two “$Format:%H$” strings will be replaced with the commit hash and the if condition will be false. On the contrary, without the next.config.js export-subst command the if statement will be true and “static-build-id” build ID will be used instead.

Conclusion

Here we looked at how to deploy a Next.js application on a multi-server environment. Using the Next.js generateBuildId feature allows you to make all instances share the same build ID. This is what Next.js uses to create a session with the client, which would be impossible without this info. next-build-id is a good solution to generate it correctly, but as shown custom logic is required when using AWS.

Thanks for reading! I hope that you found my story helpful.

nv-author-image

Antonello Zanini

I'm a software engineer, but I prefer to call myself a Technology Bishop. Spreading knowledge through writing is my mission.View Author posts

Want technical content like this in your blog?