How to run Next.js in production: a technical guide.

Deploying a Next.js application into production can be straightforward yet technical, offering a range of deployment strategies. Whether you opt for a managed solution or prefer self-hosting using Node.js, Docker, or static exports, Next.js equips you with the flexibility and performance required for modern web applications. This guide provides a step-by-step approach to deploying Next.js in production, including advanced caching and middleware.

Challenges in production deployments

Deploying web applications to production introduces various challenges:

  • Performance optimization for end users across diverse geographies.

  • Scalability to handle increasing traffic.

  • Seamless updates without downtime or data inconsistency.

Traditional frameworks may not natively support features like server-side rendering (SSR) or incremental static regeneration (ISR), often requiring complex setups. Next.js simplifies this process, offering both managed and self-hosted options to suit your needs.

Next.js deployment options

Production builds with next build

Before deployment, run next build to generate an optimized production build. This process:

  • Compiles JavaScript and minifies it using the Next.js Compiler.

  • Generates static HTML and CSS for each page.

  • Ensures compatibility across modern browsers.

The resulting build is consistent, supporting all Next.js features.

Deployment methods

Node.js Server

To deploy with Node.js:

  1. Ensure your package.json includes:
{  
  "scripts": {  
    "build": "next build",  
    "start": "next start"  
  }  
}
  1. Build your app:
npm run build
  1. Start the server:
npm run start

This approach supports all Next.js features, including SSR, ISR, and dynamic routing.

Docker Deployment

Deploying via Docker allows for containerized environments, ideal for scalable applications:

  1. Build the Docker image:
docker build -t nextjs-app .  
  1. Run the container:
docker run -p 3000:3000 nextjs-app  

Docker ensures consistency across development, staging, and production environments.

Static Export

For purely static sites or Single-Page Applications (SPAs), Next.js supports static exports:

  1. Add a static export script in package.json:
{  
  "scripts": {  
    "export": "next build && next export"  
  }  
}  
  1. Run the script:
npm run export  

This method is compatible with hosting solutions like AWS S3, Nginx, or GitHub Pages, but lacks server-dependent features like SSR.

Next.js benefits and advanced features in production

Caching and Incremental Static Regeneration (ISR)

Next.js leverages caching to optimize performance:

  • Immutable assets are cached indefinitely, improving load times.

  • ISR allows you to update static pages without a full rebuild. The Cache-Control headers ensure revalidation based on your getStaticProps configuration.

Example:

export async function getStaticProps() {  
  return {  
    props: {},  
    revalidate: 60, // Revalidate every 60 seconds  
  }  
}  

Middleware

In Next.js, middleware allows for flexible request handling, including tasks like redirects, authentication, and custom headers. It operates with minimal latency and supports self-hosted setups.

Example (middleware.js) :

import { NextResponse } from "next/server";
export function middleware(req) {
  const url = req.nextUrl.clone();
  // Redirect to the login page if the user is not authenticated
  if (!req.cookies.get("auth")) {
    url.pathname = "/login";
    return NextResponse.redirect(url);
  }
  // Proceed with the request if the user is authenticated
  return NextResponse.next();
}

Prefetching

Next.js automatically prefetches data and assets for links that are visible in the viewport, enabling faster navigation in production environments. Using the Link component ensures that linked pages are preloaded in the background, improving navigation speed. Example of prefetching with Link:

import Link from "next/link";
export default function Home() {
  return (
    <div>
      <Link href="/about">Go to About Page</Link>
    </div>
  );
}

Image Optimization

Next.js provides built-in image optimization to serve images in modern formats like WebP, adjust them for device-specific sizes, and enhance performance. The Image component automatically handles lazy loading, resizing, and format selection. It also offers advanced features like placeholders (e.g., blur effects for loading). Example of using Image:

import Image from "next/image";
export default function Profile() {
  return (
    <Image
      src="/profile.jpg"
      alt="Profile Picture"
      width={150}
      height={150}
      placeholder="blur"
      blurDataURL="/path-to-low-resolution-image/placeholder.jpg"
    />
  );
}

To protect your application from malicious users, configuration is required in order to use external images. The following configuration should be added to your next.config.js file:

module.exports = {
  images: {
    /* The following configuration ensures the src property of an <Image> must start with https://example.com/account123/ */
    remotePatterns: [
      {
        protocol: "https",
        hostname: "example.com",
        port: "",
        pathname: "/account123/**",
        search: "",
      },
    ],
  },
};

Deploying Next.js in production provides the flexibility to choose between managed hosting and self-hosted solutions, ensuring scalability, performance, and robust feature support. By leveraging caching, middleware, and advanced deployment strategies, you can optimize your applications for high availability and user satisfaction.

For more details go to: Building Your Application: Deploying

Looking to streamline your Next.js deployment process? Partner with our expert team to ensure your application is production-ready, scalable, and optimized for performance. Contact us today to get started!

Juan Cruz Dominici
Juan Cruz Dominici

Software Engineer.