This article teaches you how to self host Nextjs websites and deploy them without using the Vecel platform to public clouds such as AWS, Azure, and Google Cloud Platform. You will learn different rendering approaches, infrastructure requirements, deployment steps, and things to consider when deploying websites.
Next.js is an open-source, production-ready, and opinionated React framework. React is only a UI library and does not provide tools to run React websites on servers. Nextjs makes it easy to build and run React applications on servers or statically.
If you are starting from scratch and do not have existing cloud infrastructure, then Vercel, CloudFlare, Netlify, and many other vendors offer SaaS services that might fit your needs.
The following section examines why you should consider self hosting a Nextjs website.
Why self host a Nextjs website?
- Avoid vendor lock-in and be in control of the hosting infrastructure.
- Already invested in AWS/Azure/Google Cloud Platform or other clouds. Adding another vendor to the mix will increase complexity and cost.
- You are introducing Nextjs into your tech stack and are unsure if it will work out. Avoid significant infrastructure changes until Nextjs is proven to work.
- Specific infrastructure needs that are not met by the Vercel platform.
It would be best to decide on the rendering strategy for your Nextjs website, as that will dictate the hosting infrastructure. The following section explains various rendering strategies for a Nextjs website.
Rendering Strategies
There are two main strategies for rendering a Nextjs website: static generation and server-side. Static rendering is appropriate when the content is not frequently updated, such as marketing pages, documentation, blogs, and catalogs. The server-side rendering is suitable for dynamic content.
Static Generation
Nextjs makes it easy to generate pages at build time and recommends it as the default and go-to approach. The statically generated websites can be hosted on a cloud storage and cached on a Content Delivery Network. This approach does not require a server and is low-cost, highly scalable, and easy to implement.
Infrastructure
To deploy a statically generated website, you need cloud storage such as AWS S3 and a Content Delivery Network such as AWS CloudFront.
Figure: Infrastructure for hosting a statically generated website
Deployment Steps
Deploying a statically generated Nextjs website requires the following steps:
1. Update package.json for static generation
Nextjs CLI provides an export command for static generation. Edit the package.json file and update the build script to include the next export.
"scripts": { "build": "next build && next export" }
2. Static Generation
Run the npm run build script to generate the static website.
npm run build
The static website is generated into a folder named out.
3. Copy files to Cloud Storage
Copy statically generated files in the out folder to cloud storage. The copied files should be publicly accessible and have caching age specified. Use the following command to copy files to AWS S3 storage.
aws s3 sync ./out s3://<bucket> --acl public-read --cache-control max-age=<seconds>
4. Invalidate CDN cache
The Content Delivery Network cache must be invalidated to serve the new files. Use the following command to invalidate a cache on AWS CloudFront.
aws cloudfront create-invalidation --distribution-id <distribution-id> --paths "/path-to-website"
Considerations
After working with large Nextjs websites with tens of thousands of pages and serving millions of visitors, I would recommend considering the following points when deciding the suitability of static generation:
- Build time can be prolonged for websites with many static web pages (100+).
- Every content change requires a new build. Static generation is not suitable for frequently updated pages. Incremental static regeneration resolves the content update issue via static generation on demand.
The beauty of Nextjs is that it allows you to decide the rendering approach on a page level. Some pages are suitable for static generation, and some require server-side rendering. Nextjs makes it easy to switch between rendering methods with minimal code changes.
The following teaches you Server Side rendering and Incremental Static regeneration approaches for rendering a page on request.
Server-side rendering (SSR)
Server-side rendering is suitable for pages with dynamic or personalized content where caching is impossible. A Nodejs server is used to generate the page on each request. The Incremental Static Regeneration (ISR) is similar but with the addition of Nextjs caching the generated page for a configurable duration.
Infrastructure
To deploy a server-side rendered Nextjs website following infrastructure pieces are needed:
- Nodejs server to generate pages on demand.
- Cloud storage to host static assets.
- Content Delivery Network to serve static assets and dynamic pages.
Figure: Infrastructure for hosting Nextjs Server-side Rendered or Incremental Static Regenerated website
You have many options for running the Nodejs server, from running a compute instance to running a docker container in Kubernetes. My recommendation is to use Docker for flexibility and scalability.
Nextjs Docker Image
The Nextjs docker-compose example is a good starting point that showcases how to package Nextjs code into a docker image. Docker opens up many deployment options, such as AWS EC2 instances, Azure App, Kubernetes, and more. Here is the code of a sample Dockerfile for a Nextjs website:
# filename: Dockerfile
# Use an official Node.js runtime as a parent image
FROM node:18-bullseye-slim
# Set the working directory to /app
WORKDIR /app
# Copy package.json and package-lock.json to the container
COPY package*.json ./
# Install dependencies
RUN npm install --production
# Copy the rest of the application code to the container
COPY . .
# Build the Next.js app
RUN npm run build
# Expose port 3000
EXPOSE 3000
# Start the Next.js app
CMD ["npm", "start"]
Copy the above code into a file name Dockerfile in the root of your Nextjs project. Run the following commands to launch a docker instance on your machine:
docker build -t my-app .
docker run -p 3000:3000 my-app
Deployment Steps
To deploy a server-side rendered Nextjs website following steps are required:
1. Build a docker image
To make deployment easy, configure Nextjs to output a standalone folder that contains the server code bundle. Then run the docker build command to generate a docker image containing the Nextjs server (the standalone folder) and client (the .next/static folder) bundles.
docker build .
2. Copy static assets to cloud storage
The .next/static folder contains static assets that run in the browser. A Nodejs server can serve this folder, but do it from a CDN for the best performance.
- Extract the .next/static folder from the docker image.
- Copy extracted folder to cloud storage.
There is no need to invalidate the CDN, as any code changes will result in new files with unique names.
3. Deploy the docker image
You can quickly deploy to most public cloud computing platforms by using Docker for packing the website. Follow the steps for your chosen computing platform. Check out the Nextjs GitHub repository that contains many implementation and integration examples.
For optimal performance on incremental static regeneration, refer to instructions on ISR self-hosting.
Server-side Rendering Considerations
- Plan compute and scalability for the Nodejs server based on performance and load requirements.
- For reliability, consider using multiple Nodejs instances behind a load balancer.
- Set up application monitoring and error tracking.
Summary
Nextjs takes the pain out of running React websites in production. Nextjs is a zero-configuration meta-framework that supports static generation, incremental static regeneration, server-side rendering, file system-based routing, and a lot more features out of the box.
There are many options for deploying and hosting Nextjs websites. Spend time upfront deciding on a suitable rendering strategy as that will define the infrastructure and deployment approach.