This article explains how to containerize a Next.js application with a Dockerfile for a customizable deployment. Alternatively, you can deploy a Next.js app to Kinsta using automatic deployment.

With Docker, we package an application, its environment, and its dependencies into an isolated container. A container consists of an application running in a Linux stripped-down version. A Docker image is the blueprint for a container, and the container is a running instance of an image.

To containerize an application, we use a declarative method through a Dockerfile. Docker reads and executes the scripts defined in this file to build and deploy your application.

Advantages of Containerizing Your Application

You have a lot of benefits from containerizing an application, such as portability, stability, scalability, security, and performance. By deploying an app to Kinsta with a Dockerfile, you also leverage its customizability.

Portability

Docker encapsulates everything an application needs to run, allowing them to be commuted easily between environments. Whether you are running it locally, on a computer with a different operating system, or in staging and production environments, Docker builds the application with the same components, making it easier to code, test, and deploy.

Scalability

With Docker, you can run several instances of your container on different servers. Container orchestrators handle increased traffic without affecting your app’s performance.

Stability

By running your app in an isolated container, you have predictable results when moving code between development, test, and production systems. Given that your container contains exact versions of necessary libraries and packages, it minimizes the risk of bugs due to different dependency revisions.

Also, when your app is deployed to a production server, Docker keeps it isolated from other applications, minimizing the risk of being affected by their traffic spikes.

Security

Docker containers provide a more secure environment for your workloads than traditional models. As they break up your applications into much smaller, loosely coupled components, each isolated from one another, there is a significantly reduced attack surface. Docker containers decrease the chance for hackers to exploit your computer systems and make it harder for a breach to spread in the event of an attack. Learn more in this article: 9 Security Best Practices for Docker Containers.

Performance

Containers do not contain a whole operating system like virtual machines and traditional servers. With that, containers have a considerably smaller footprint and are faster to construct and start.

Custom Deployment

With Kinsta, you can deploy your applications automatically by using Buildpacks and Nixpacks. But when the build process is automatically triggered based on your app’s codebase, you don’t have a lot of room for customizations. If you deploy to Kinsta with a Dockerfile, you can configure precisely how you want to build and deploy your application.

Requirements

To deploy a Next.js application with Docker, you need:

Start With a Next.js Application

If you are starting from an existing application, you can skip this step. If you are starting fresh, create a new Next.js application:

  1. Open your terminal and install create-next-app:
npm i -g create-next-app@latest
  1. Navigate to the directory where you want to install it and create a new Next.js application in its own directory:
npx create-next-app@latest new-app

Next prompts you to specify a number of configuration options for your new app. For this tutorial, you can simply accept the suggested defaults.

  1. To preview your new application, navigate to the new-app directory and run:
npm run dev

For your reference, we created an example application using this method.

Containerize a Next.js App With a Dockerfile

To containerize your Next.js application and deploy it with Docker, create a Dockerfile in your app’s root directory.

Build Stage

In your Dockerfile, start by creating the app’s build stage to build your application:

  1. Use the official latest stable Node.js alpine image as the base image for the build stage:
FROM node:18-alpine AS build
WORKDIR /app
  1. Copy the package.json and package-lock.json files to the container:
COPY package*.json ./
  1. Install the app dependencies with:
RUN npm ci
  1. Copy the rest of the application code to the container with:
COPY . .
  1. Build the application:
RUN npm run build

Runtime Stage

Create the runtime stage to deploy your application:

  1. Use the official latest stable Node.js alpine image as the base image for the runtime stage:
FROM node:18-alpine AS runtime
  1. Set the working directory to /app:
WORKDIR /app
  1. Copy the package.json and package-lock.json files to the container:
COPY package*.json ./
  1. Install only production dependencies:
RUN npm ci --only=production
  1. Copy the built app from the build stage to the runtime stage:
COPY --from=build /app/.next ./.next
  1. Copy the public folder from the build stage to the runtime stage:
COPY --from=build /app/public ./public
  1. Expose port 3000:
EXPOSE 3000
  1. Run the container as an unprivileged user:
USER node
  1. Start the Next.js app:
CMD ["npm", "start"]

We end up with the following Dockerfile:

FROM node:18-alpine AS build

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine AS runtime

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=build /app/.next ./.next
COPY --from=build /app/public ./public

EXPOSE 3000
USER node
CMD ["npm", "start"]

Deploy the App Locally With Docker

Although you can still preview your application with run npm dev, run it locally with Docker to mimic the production environment and to test and preview any changes you make to your app’s Dockerfile.

To preview your application:

  1. Build the app with docker build:
docker build -t next-docker .
  1. Run the container to preview your app:
docker run -p 3000:3000 next-docker
  1. On your browser, open http://localhost:3000.

Deploy the Containerized Next.js App to Kinsta

Follow the steps below to deploy your application to Kinsta using its Dockerfile.

  1. Host your app’s codebase on a Git repository (on GitHub, GitLab, or Bitbucket).
  2. Log into your MyKinsta account or create a new one to access your dashboard.
  3. Click Add service and select Application.
  4. Select your Git provider, repository, and the branch you want to deploy from.
  5. Check the Automatic deployment on commit checkbox if you’d like to deploy your app at every push to your repo.
  6. Select the data center closest to your audience and click Continue.
  7. Choose your build environment and select Use Dockerfile to set up container image.
  8. If your Dockerfile isn’t in your repo’s root, use Context to indicate its path and click Continue.
  9. You can leave the Start command entry empty. Kinsta uses npm start to start your application.
  10. Select the pod size and number of instances best suited for your app and click Continue.
  11. Fill out your credit card information and click Create application.

Your application is ready for deployment. If you checked the checkbox Automatic deployment on commit on step 5, Kinsta starts the deployment automatically.

Summary

In this article, we discussed a few advantages of using Docker over traditional models; we explained how to create a Dockerfile for a Next.js app, build and deploy it with Docker locally, and deploy it to Kinsta.

Kinsta’s application hosting makes your development workflow effortless and efficient.

Features such as containerized apps on GCP infrastructure running on C2 machines with 35 data centers available, premium integration with Cloudflare for a high-performance CDN that serves your site from 260+ Points of Presence, enterprise-level firewall DDoS protection, Edge Caching, and uptime monitoring (with 99% uptime guarantee), ensure your app runs fast, secure, and is reliably available to the internet.

Marcia Ramos

I'm the Editorial Team Lead at Kinsta. I'm a open source enthusiast and I love coding. With more than 7 years of technical writing and editing for the tech industry, I love collaborating with people to create clear and concise pieces of content and improve workflows.