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.