Share feedback
Answers are generated based on the documentation.

Containerize a Next.js Application

Prerequisites

Before you begin, make sure the following tools are installed and available on your system:

  • You have installed the latest version of Docker Desktop.
  • You have a git client. The examples in this section use a command-line based git client, but you can use any client.

New to Docker?
Start with the Docker basics guide to get familiar with key concepts like images, containers, and Dockerfiles.


Overview

This guide walks you through containerizing a Next.js application with Docker. You'll learn how to create a production-ready Docker image using best practices that improve performance, security, scalability, and deployment efficiency.

By the end of this guide, you will:

  • Containerize a Next.js application using Docker.
  • Create and optimize a Dockerfile for production builds.
  • Use multi-stage builds to minimize image size.
  • Leverage Next.js standalone or export output for efficient containerization.
  • Follow best practices for building secure and maintainable Docker images.

Get the sample application

Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the git repository:

$ git clone https://github.com/kristiyan-velkov/docker-nextjs-sample

Generate a Dockerfile

Docker provides an interactive CLI tool called docker init that helps scaffold the necessary configuration files for containerizing your application. This includes generating a Dockerfile, .dockerignore, compose.yaml, and README.Docker.md.

To begin, navigate to the project directory:

$ cd docker-nextjs-sample

Then run the following command:

$ docker init

You'll see output similar to:

Welcome to the Docker Init CLI!

This utility will walk you through creating the following files with sensible defaults for your project:
  - .dockerignore
  - Dockerfile
  - compose.yaml
  - README.Docker.md

Let's get started!

The CLI will prompt you with a few questions about your app setup. For consistency, please use the same responses shown in the example below when prompted:

QuestionAnswer
What application platform does your project use?Node
What version of Node do you want to use?24.14.0-alpine
Which package manager do you want to use?npm
Do you want to run "npm run build" before starting server?yes
What directory is your build output to?.next
What command do you want to use to start the app?npm run start
What port does your server listen on?3000

After completion, your project directory will contain the following new files:

├── docker-nextjs-sample/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── compose.yaml
│ └── README.Docker.md

Build the Docker image

The default Dockerfile generated by docker init serves as a solid starting point for general Node.js applications. However, Next.js has specific requirements for production deployments. This guide shows two approaches: standalone output (Node.js server) and export output (static files with NGINX).

Step 1: Review the generated files

In this step, you'll add a Dockerfile and configuration files by following best practices:

  • Use multi-stage builds to keep the final image clean and small
  • Standalone: Node.js runs the Next.js server; Export: NGINX serves static files from the export
  • Improve performance and security by only including what's needed

These updates help ensure your app is easy to deploy, fast to load, and production-ready.

Note

A Dockerfile is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment.
For full details, see the Dockerfile reference.

Step 2: Configure Next.js output and Dockerfile

Before creating a Dockerfile, choose a base image: the Node.js Official Image or a Docker Hardened Image (DHI) from the Hardened Image catalog. Choosing DHI gives you a production-ready, lightweight, and secure image. For more information, see Docker Hardened Images.

Important

This guide uses stable Node.js LTS image tags that are considered secure when the guide is written. Because new releases and security patches are published regularly, always review the official Node.js Docker images and select a secure, up-to-date version before building or deploying.


2.1 Next.js with standalone output

Standalone output (output: "standalone") makes Next.js build a self-contained output that includes only the files and dependencies needed to run the application. A single node server.js can serve the app, which is ideal for Docker and supports server-side rendering, API routes, and incremental static regeneration. For details, see the Next.js output configuration documentation (including the "standalone" option).

The container runs the Next.js server with Node.js on port 3000.

Configure Next.js — Open or create next.config.ts in your project root:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "standalone",
};

export default nextConfig;

Choose either a Docker Hardened Image or the Docker Official Image, then create or replace your Dockerfile with the content from the selected tab below.

Docker Hardened Images (DHIs) are available for Node.js in the Docker Hardened Images catalog. For more information, see the DHI quickstart guide.

  1. Sign in to the DHI registry:

    $ docker login dhi.io
    
  2. Pull the Node.js DHI (check the catalog for available versions):

    $ docker pull dhi.io/node:24-alpine3.22-dev
    
  3. Replace the generated Dockerfile with the following. The FROM instructions use dhi.io/node:24-alpine3.22-dev. Check the Docker Hardened Images catalog for the latest versions and update the image tags as needed for security and compatibility.

    # ============================================
    # Stage 1: Dependencies Installation Stage
    # ============================================
    
    # IMPORTANT: Docker Hardened Image (DHI) Version Maintenance
    # This Dockerfile uses dhi.io/node. Regularly validate and update to the latest DHI versions in the catalog for security and compatibility.
    
    FROM dhi.io/node:24-alpine3.22-dev AS dependencies
    
    # Set working directory
    WORKDIR /app
    
    # Copy package-related files first to leverage Docker's caching mechanism
    COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
    
    # Install project dependencies with frozen lockfile for reproducible builds
    RUN --mount=type=cache,target=/root/.npm \
        --mount=type=cache,target=/usr/local/share/.cache/yarn \
        --mount=type=cache,target=/root/.local/share/pnpm/store \
      if [ -f package-lock.json ]; then \
        npm ci --no-audit --no-fund; \
      elif [ -f yarn.lock ]; then \
        corepack enable yarn && yarn install --frozen-lockfile --production=false; \
      elif [ -f pnpm-lock.yaml ]; then \
        corepack enable pnpm && pnpm install --frozen-lockfile; \
      else \
        echo "No lockfile found." && exit 1; \
      fi
    
    # ============================================
    # Stage 2: Build Next.js application in standalone mode
    # ============================================
    
    FROM dhi.io/node:24-alpine3.22-dev AS builder
    
    # Set working directory
    WORKDIR /app
    
    # Copy project dependencies from dependencies stage
    COPY --from=dependencies /app/node_modules ./node_modules
    
    # Copy application source code
    COPY . .
    
    ENV NODE_ENV=production
    
    # Next.js collects completely anonymous telemetry data about general usage.
    # Learn more here: https://nextjs.org/telemetry
    # Uncomment the following line in case you want to disable telemetry during the build.
    # ENV NEXT_TELEMETRY_DISABLED=1
    
    # Build Next.js application
    # If you want to speed up Docker rebuilds, you can cache the build artifacts
    # by adding: --mount=type=cache,target=/app/.next/cache
    # This caches the .next/cache directory across builds, but it also prevents
    # .next/cache/fetch-cache from being included in the final image, meaning
    # cached fetch responses from the build won't be available at runtime.
    RUN if [ -f package-lock.json ]; then \
        npm run build; \
      elif [ -f yarn.lock ]; then \
        corepack enable yarn && yarn build; \
      elif [ -f pnpm-lock.yaml ]; then \
        corepack enable pnpm && pnpm build; \
      else \
        echo "No lockfile found." && exit 1; \
      fi
    
    # ============================================
    # Stage 3: Run Next.js application
    # ============================================
    
    FROM dhi.io/node:24-alpine3.22-dev AS runner
    
    # Set working directory
    WORKDIR /app
    
    # Set production environment variables
    ENV NODE_ENV=production
    ENV PORT=3000
    ENV HOSTNAME="0.0.0.0"
    
    # Next.js collects completely anonymous telemetry data about general usage.
    # Learn more here: https://nextjs.org/telemetry
    # Uncomment the following line in case you want to disable telemetry during the run time.
    # ENV NEXT_TELEMETRY_DISABLED=1
    
    # Copy production assets
    COPY --from=builder --chown=node:node /app/public ./public
    
    # Set the correct permission for prerender cache
    RUN mkdir .next
    RUN chown node:node .next
    
    # Automatically leverage output traces to reduce image size
    # https://nextjs.org/docs/advanced-features/output-file-tracing
    COPY --from=builder --chown=node:node /app/.next/standalone ./
    COPY --from=builder --chown=node:node /app/.next/static ./.next/static
    
    # If you want to persist the fetch cache generated during the build so that
    # cached responses are available immediately on startup, uncomment this line:
    # COPY --from=builder --chown=node:node /app/.next/cache ./.next/cache
    
    # Switch to non-root user for security best practices
    USER node
    
    # Expose port 3000 to allow HTTP traffic
    EXPOSE 3000
    
    # Start Next.js standalone server
    CMD ["node", "server.js"]

Create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following (uses node):

  # ============================================
  # Stage 1: Dependencies Installation Stage
  # ============================================

  ARG NODE_VERSION=24.14.0-slim

  FROM node:${NODE_VERSION} AS dependencies

  # Set working directory
  WORKDIR /app

  # Copy package-related files first to leverage Docker's caching mechanism
  COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./

  # Install project dependencies with frozen lockfile for reproducible builds
  RUN --mount=type=cache,target=/root/.npm \
      --mount=type=cache,target=/usr/local/share/.cache/yarn \
      --mount=type=cache,target=/root/.local/share/pnpm/store \
    if [ -f package-lock.json ]; then \
      npm ci --no-audit --no-fund; \
    elif [ -f yarn.lock ]; then \
      corepack enable yarn && yarn install --frozen-lockfile --production=false; \
    elif [ -f pnpm-lock.yaml ]; then \
      corepack enable pnpm && pnpm install --frozen-lockfile; \
    else \
      echo "No lockfile found." && exit 1; \
    fi

  # ============================================
  # Stage 2: Build Next.js application in standalone mode
  # ============================================

  FROM node:${NODE_VERSION} AS builder

  # Set working directory
  WORKDIR /app

  # Copy project dependencies from dependencies stage
  COPY --from=dependencies /app/node_modules ./node_modules

  # Copy application source code
  COPY . .

  ENV NODE_ENV=production

  # Next.js collects completely anonymous telemetry data about general usage.
  # Learn more here: https://nextjs.org/telemetry
  # Uncomment the following line in case you want to disable telemetry during the build.
  # ENV NEXT_TELEMETRY_DISABLED=1

  # Build Next.js application
  # If you want to speed up Docker rebuilds, you can cache the build artifacts
  # by adding: --mount=type=cache,target=/app/.next/cache
  # This caches the .next/cache directory across builds, but it also prevents
  # .next/cache/fetch-cache from being included in the final image, meaning
  # cached fetch responses from the build won't be available at runtime.
  RUN if [ -f package-lock.json ]; then \
      npm run build; \
    elif [ -f yarn.lock ]; then \
      corepack enable yarn && yarn build; \
    elif [ -f pnpm-lock.yaml ]; then \
      corepack enable pnpm && pnpm build; \
    else \
      echo "No lockfile found." && exit 1; \
    fi

  # ============================================
  # Stage 3: Run Next.js application
  # ============================================

  FROM node:${NODE_VERSION} AS runner

  # Set working directory
  WORKDIR /app

  # Set production environment variables
  ENV NODE_ENV=production
  ENV PORT=3000
  ENV HOSTNAME="0.0.0.0"

  # Next.js collects completely anonymous telemetry data about general usage.
  # Learn more here: https://nextjs.org/telemetry
  # Uncomment the following line in case you want to disable telemetry during the run time.
  # ENV NEXT_TELEMETRY_DISABLED=1

  # Copy production assets
  COPY --from=builder --chown=node:node /app/public ./public

  # Set the correct permission for prerender cache
  RUN mkdir .next
  RUN chown node:node .next

  # Automatically leverage output traces to reduce image size
  # https://nextjs.org/docs/advanced-features/output-file-tracing
  COPY --from=builder --chown=node:node /app/.next/standalone ./
  COPY --from=builder --chown=node:node /app/.next/static ./.next/static

  # If you want to persist the fetch cache generated during the build so that
  # cached responses are available immediately on startup, uncomment this line:
  # COPY --from=builder --chown=node:node /app/.next/cache ./.next/cache

  # Switch to non-root user for security best practices
  USER node

  # Expose port 3000 to allow HTTP traffic
  EXPOSE 3000

  # Start Next.js standalone server
  CMD ["node", "server.js"]
Note

This Dockerfile uses three stages: dependencies, builder, and runner. The final image runs node server.js and listens on port 3000.


2.2 Next.js with export output

Output export (output: "export") makes Next.js build a fully static site at build time. It generates HTML, CSS, and JavaScript into an out directory that can be served by any static host or CDN—no Node.js server at runtime. Use this when you don't need server-side rendering or API routes. For details, see the Next.js output configuration documentation.

Configure Next.js — Open next.config.ts in your project root and add the following code:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "export",
  trailingSlash: true,
  images: {
    unoptimized: true,
  },
};

export default nextConfig;

Choose either a Docker Hardened Image or the Docker Official Image, then replace your Dockerfile with the content from the selected tab below.

Docker Hardened Images (DHIs) are available for Node.js and NGINX in the Docker Hardened Images catalog. For more information, see the DHI quickstart guide.

  1. Sign in to the DHI registry:

    $ docker login dhi.io
    
  2. Pull the Node.js DHI (check the catalog for available versions):

    $ docker pull dhi.io/node:24-alpine3.22-dev
    
  3. Pull the Nginx DHI (check the catalog for available versions):

    $ docker pull dhi.io/nginx:1.28.0-alpine3.21-dev
    
  4. Replace the generated Dockerfile with the following. The FROM instructions use Docker Hardened Images: dhi.io/node:24-alpine3.22-dev and dhi.io/nginx:1.28.0-alpine3.21-dev. Check the Docker Hardened Images catalog for the latest versions and update the image tags as needed for security and compatibility.

    # ============================================
    # Stage 1: Dependencies Installation Stage
    # ============================================
    
    # IMPORTANT: Docker Hardened Image (DHI) Version Maintenance
    # This Dockerfile uses dhi.io/node and dhi.io/nginx. Regularly validate and update to the latest DHI versions in the catalog for security and compatibility.
    
    FROM dhi.io/node:24-alpine3.22-dev AS dependencies
    
    # Set the working directory
    WORKDIR /app
    
    # Copy package-related files first to leverage Docker's caching mechanism
    COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
    
    # Install project dependencies with frozen lockfile for reproducible builds
    RUN --mount=type=cache,target=/root/.npm \
        --mount=type=cache,target=/usr/local/share/.cache/yarn \
        --mount=type=cache,target=/root/.local/share/pnpm/store \
      if [ -f package-lock.json ]; then \
        npm ci --no-audit --no-fund; \
      elif [ -f yarn.lock ]; then \
        corepack enable yarn && yarn install --frozen-lockfile --production=false; \
      elif [ -f pnpm-lock.yaml ]; then \
        corepack enable pnpm && pnpm install --frozen-lockfile; \
      else \
        echo "No lockfile found." && exit 1; \
      fi
    
    # ============================================
    # Stage 2: Build Next.js Application
    # ============================================
    
    FROM dhi.io/node:24-alpine3.22-dev AS builder
    
    # Set the working directory
    WORKDIR /app
    
    # Copy project dependencies from dependencies stage
    COPY --from=dependencies /app/node_modules ./node_modules
    
    # Copy application source code
    COPY . .
    
    ENV NODE_ENV=production
    
    # Next.js collects completely anonymous telemetry data about general usage.
    # Learn more here: https://nextjs.org/telemetry
    # Uncomment the following line in case you want to disable telemetry during the build.
    # ENV NEXT_TELEMETRY_DISABLED=1
    
    # Build Next.js application
    RUN --mount=type=cache,target=/app/.next/cache \
      if [ -f package-lock.json ]; then \
        npm run build; \
      elif [ -f yarn.lock ]; then \
        corepack enable yarn && yarn build; \
      elif [ -f pnpm-lock.yaml ]; then \
        corepack enable pnpm && pnpm build; \
      else \
        echo "No lockfile found." && exit 1; \
      fi
    
    # =========================================
    # Stage 3: Serve Static Files with Nginx
    # =========================================
    
    FROM dhi.io/nginx:1.28.0-alpine3.21-dev AS runner
    
    # Set the working directory
    WORKDIR /app
    
    # Next.js collects completely anonymous telemetry data about general usage.
    # Learn more here: https://nextjs.org/telemetry
    # Uncomment the following line in case you want to disable telemetry during the run time.
    # ENV NEXT_TELEMETRY_DISABLED=1
    
    # Copy custom Nginx config
    COPY nginx.conf /etc/nginx/nginx.conf
    
    # Copy the static build output from the build stage to Nginx's default HTML serving directory
    COPY --chown=nginx:nginx --from=builder /app/out /usr/share/nginx/html
    
    # Non-root user for security best practices
    USER nginx
    
    # Expose port 8080 to allow HTTP traffic
    EXPOSE 8080
    
    # Start Nginx directly with custom config
    ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"]
    CMD ["-g", "daemon off;"]

Create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following (uses node and nginxinc/nginx-unprivileged):

# ============================================
# Stage 1: Dependencies Installation Stage
# ============================================

ARG NODE_VERSION=24.14.0-slim
ARG NGINXINC_IMAGE_TAG=alpine3.22

FROM node:${NODE_VERSION} AS dependencies

# Set the working directory
WORKDIR /app

# Copy package-related files first to leverage Docker's caching mechanism
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./

# Install project dependencies with frozen lockfile for reproducible builds
RUN --mount=type=cache,target=/root/.npm \
    --mount=type=cache,target=/usr/local/share/.cache/yarn \
    --mount=type=cache,target=/root/.local/share/pnpm/store \
  if [ -f package-lock.json ]; then \
    npm ci --no-audit --no-fund; \
  elif [ -f yarn.lock ]; then \
    corepack enable yarn && yarn install --frozen-lockfile --production=false; \
  elif [ -f pnpm-lock.yaml ]; then \
    corepack enable pnpm && pnpm install --frozen-lockfile; \
  else \
    echo "No lockfile found." && exit 1; \
  fi

# ============================================
# Stage 2: Build Next.js Application
# ============================================

FROM node:${NODE_VERSION} AS builder

# Set the working directory
WORKDIR /app

# Copy project dependencies from dependencies stage
COPY --from=dependencies /app/node_modules ./node_modules

# Copy application source code
COPY . .

ENV NODE_ENV=production

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1

# Build Next.js application
RUN --mount=type=cache,target=/app/.next/cache \
  if [ -f package-lock.json ]; then \
    npm run build; \
  elif [ -f yarn.lock ]; then \
    corepack enable yarn && yarn build; \
  elif [ -f pnpm-lock.yaml ]; then \
    corepack enable pnpm && pnpm build; \
  else \
    echo "No lockfile found." && exit 1; \
  fi

# =========================================
# Stage 3: Serve Static Files with Nginx
# =========================================

FROM nginxinc/nginx-unprivileged:${NGINXINC_IMAGE_TAG} AS runner

# Set the working directory
WORKDIR /app

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the run time.
# ENV NEXT_TELEMETRY_DISABLED=1

# Copy custom Nginx config
COPY nginx.conf /etc/nginx/nginx.conf

# Copy the static build output from the build stage to Nginx's default HTML serving directory
COPY --from=builder /app/out /usr/share/nginx/html

# Non-root user for security best practices
USER nginx

# Expose port 8080 to allow HTTP traffic
EXPOSE 8080

# Start Nginx directly with custom config
ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"]
CMD ["-g", "daemon off;"]
Note

We use nginx-unprivileged instead of the standard NGINX image to run as a non-root user, following security best practices.

  1. Create nginx.conf (required for export output only) — Create a file named nginx.conf in the root of your project:

    # Minimal Nginx config for static Next.js app
    worker_processes 1;
    
    # Store PID in /tmp (always writable)
    pid /tmp/nginx.pid;
    
    events {
        worker_connections 1024;
    }
    
    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;
    
        # Disable logging to avoid permission issues
        access_log off;
        error_log  /dev/stderr;
    
        # Optimize static file serving
        sendfile        on;
        tcp_nopush      on;
        tcp_nodelay     on;
        keepalive_timeout  65;
    
        # Gzip compression
        gzip on;
        gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
        gzip_min_length 256;
    
        server {
            listen       8080;
            server_name  localhost;
    
            # Serve static files
            root /usr/share/nginx/html;
            index index.html;
    
            # Handle Next.js static export routing
            # See: https://nextjs.org/docs/app/guides/static-exports#deploying
            location / {
                try_files $uri $uri.html $uri/ =404;
            }
    
            # This is necessary when `trailingSlash: false` (default).
            # You can omit this when `trailingSlash: true` in next.config.
            # Handles nested routes like /blog/post -> /blog/post.html
            location ~ ^/(.+)/$ {
                rewrite ^/(.+)/$ /$1.html break;
            }
    
            # Serve Next.js static assets
            location ~ ^/_next/ {
                try_files $uri =404;
                expires 1y;
                add_header Cache-Control "public, immutable";
            }
    
            # Optional 404 handling
            error_page 404 /404.html;
            location = /404.html {
                internal;
            }
        }
    }
    Note

    Export uses port 8080. For more details, see the Next.js output configuration and NGINX documentation.

Step 3: Configure the .dockerignore file

The .dockerignore file tells Docker which files and folders to exclude when building the image.

Note

This helps:

  • Reduce image size
  • Speed up the build process
  • Prevent sensitive or unnecessary files (like .env, .git, or node_modules) from being added to the final image.

To learn more, visit the .dockerignore reference.

Copy and replace the contents of your existing .dockerignore with the configuration below:

# Dependencies (installed inside the image, never copy from host)
node_modules/
.pnp/
.pnp.js
.pnpm-store/

# Next.js build output (generated during the image build)
.next/
out/
dist/
build/
.vercel/

# Testing (not needed in the production image)
coverage/
.nyc_output/
__tests__/
__mocks__/
jest/
cypress/
playwright-report/
test-results/
.vitest/

# Environment files (avoid leaking secrets into the build context)
.env
.env*
.env.local
.env.development.local
.env.test.local
.env.production.local

# Debug and log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
*.log

# IDE and editor files
.vscode/
.idea/
.cursor/
.cursorrules
.copilot/
*.swp
*.swo
*~

# Git
.git/
.gitignore
.gitattributes

# Docker files (reduce build context; not needed inside the image)
Dockerfile*
.dockerignore
docker-compose*.yml
compose*.yaml

# Documentation (not needed in the image)
*.md
docs/

# CI/CD (not needed in the image)
.github/
.gitlab-ci.yml
.travis.yml
.circleci/
Jenkinsfile

# TypeScript and build metadata
*.tsbuildinfo

# Cache and temporary directories
.cache/
.parcel-cache/
.eslintcache
.stylelintcache
.turbo/
.tmp/
.temp/

# Sensitive or dev-only config (optional; omit if your build needs these)
.pem
.editorconfig
.prettierrc*
.eslintrc*
.stylelintrc*
.babelrc*
*.iml

# OS-specific files
.DS_Store
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
Desktop.ini

Step 4: Build the Next.js application image

With your custom configuration in place, you're now ready to build the Docker image. Use the Dockerfile you chose in Step 3 (standalone or export).

The setup includes:

  • Multi-stage builds for optimized image size
  • Standalone: Node.js server on port 3000; Export: NGINX serving static files on port 8080
  • Non-root user for enhanced security
  • Proper file permissions and ownership

After completing the previous steps, your project directory should contain at least the following files (export also requires nginx.conf):

├── docker-nextjs-sample/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── compose.yaml
│ ├── next.config.ts
│ └── README.Docker.md

Now that your Dockerfile is configured, you can build the Docker image for your Next.js application.

Note

The docker build command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the build context).

Run the following command from the root of your project:

$ docker build --tag nextjs-sample .

What this command does:

  • Uses the Dockerfile in the current directory (.)
  • Packages the application and its dependencies into a Docker image
  • Tags the image as nextjs-sample so you can reference it later

Step 5: View local images

After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or Docker Desktop. Since you're already working in the terminal, let's use the Docker CLI.

To list all locally available Docker images, run the following command:

$ docker images

Example Output:

REPOSITORY                TAG               IMAGE ID       CREATED         SIZE
nextjs-sample             latest            8c5fc80f098e   14 seconds ago   130MB

This output provides key details about your images:

  • Repository – The name assigned to the image.
  • Tag – A version label that helps identify different builds (e.g., latest).
  • Image ID – A unique identifier for the image.
  • Created – The timestamp indicating when the image was built.
  • Size – The total disk space used by the image.

If the build was successful, you should see nextjs-sample image listed.


Run the containerized application

In the previous step, you created a Dockerfile for your Next.js application and built a Docker image using the docker build command. Now it's time to run that image in a container and verify that your application works as expected.

Run the following command in a terminal. Use the port that matches your setup: standalone uses port 3000, export uses port 8080.

$ docker run -p 3000:3000 nextjs-sample

For export output, use port 8080 instead:

$ docker run -p 8080:8080 nextjs-sample

Open a browser and view the application: http://localhost:3000 for standalone or http://localhost:8080 for export. You should see your Next.js web application.

Press ctrl+c in the terminal to stop your application.

Run the application in the background

You can run the application detached from the terminal by adding the -d option and --name to give the container a name so you can stop it later:

$ docker run -d -p 3000:3000 --name nextjs-app nextjs-sample

For export output, use port 8080:

$ docker run -d -p 8080:8080 --name nextjs-app nextjs-sample

Open a browser and view the application: http://localhost:3000 for standalone or http://localhost:8080 for export. You should see your web application.

To confirm that the container is running, use the docker ps command:

$ docker ps

This will list all active containers along with their ports, names, and status. Look for a container exposing port 3000 (standalone) or 8080 (export).

Example Output:

CONTAINER ID   IMAGE           COMMAND                  CREATED             STATUS             PORTS                    NAMES
f49b74736a9d   nextjs-sample   "node server.js"         About a minute ago   Up About a minute   0.0.0.0:3000->3000/tcp nextjs-app

To stop the application, run:

$ docker stop nextjs-app
Note

For more information about running containers, see the docker run CLI reference and the docker stop CLI reference.


Summary

In this guide, you learned how to containerize, build, and run a Next.js application using Docker. By following best practices, you created a secure, optimized, and production-ready setup.

What you accomplished:

  • Initialized your project using docker init to scaffold essential Docker configuration files.
  • Configured Next.js for either standalone output (Node.js server) or export output (static files with NGINX).
  • Added a multi-stage Dockerfile for your chosen approach: standalone (port 3000) or export (port 8080, with nginx.conf).
  • Replaced the default .dockerignore file to exclude unnecessary files and keep the image clean and efficient.
  • Built your Docker image using docker build.
  • Ran the container using docker run with the image name nextjs-sample, both in the foreground and in detached mode.
  • Verified that the app was running by visiting http://localhost:3000 (standalone) or http://localhost:8080 (export).
  • Learned how to stop the containerized application using docker stop nextjs-app.

You now have a fully containerized Next.js application, running in a Docker container, and ready for deployment across any environment with confidence and consistency.


Explore official references and best practices to sharpen your Docker workflow:


Next steps

With your Next.js application now containerized, you're ready to move on to the next step.

In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine.