How to Deploy Next.js on Multiple Servers
Tags: aws, javascript, nextjs, softwaredevelopment
Published: 2024-01-19 21:14:35

Multi-server deployments in [Next.js](https://nextjs.org/) are tricky. Without the right configuration, every time your browser is served with a different instance by the [load balancer](https://en.wikipedia.org/wiki/Load_balancing_%28computing%29), 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](https://nextjs.org/docs/api-reference/next.config.js/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](https://nextjs.org/docs/api-reference/next.config.js/configuring-the-build-id), Next.js allows you to define a custom build ID. Specifically, you can do it by using the [`generateBuildId`](https://nextjs.org/docs/api-reference/next.config.js/configuring-the-build-id) property inside your [`next.config.js`](https://nextjs.org/docs/api-reference/next.config.js/introduction) file, as follows:

```javascript
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`](https://www.npmjs.com/package/next-build-id) npm package. First, add it to your project's dependencies with the following command:

```shell
npm install next-build-id
```

Then, you can use it as follows:

```javascript
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:

```shell
git rev-parse HEAD
```

The `next-build-id` also offers other options. Learn more about it on [its official GitHub page](https://github.com/nexdrew/next-build-id).

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](https://aws.amazon.com/elasticbeanstalk/), 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`](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb3-deploy.html) 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](https://github.com/flippidippi) GitHub user found a workaround. As explained [here](https://github.com/vercel/next.js/issues/786#issuecomment-393303217), 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:

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

Then, create a [`.gitattributes`](https://git-scm.com/docs/gitattributes) file in your root directory and initialize it with the following line:

```shell
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.
  
***
_The post "[How to Deploy Next.js on Multiple Servers](https://writech.run/blog/how-to-deploy-next-js-on-multiple-servers-3b493d4ce0e9/)" appeared first on [Writech](https://writech.run)._