Running NextJS in a Docker Container
Published: 2023-04-13 10:49:49

---
title: Running NextJS in a Docker Container
published: true
date: 2023-04-13 17:12:33 UTC
tags:
canonical_url: https://www.darraghoriordan.com/2023/04/13/running-next-js-docker-container/
---

## Introduction

This is a guide on how to run a NextJS application in a Docker container.

NextJS is created and maintained by Vercel so it works great on their hosting service. Vercel is an amazing service that allows you to deploy your NextJS application with ease.

However, there are times when you may want to run your application in a Docker container. Using a docker container will allow you to host a NextJS application on Azure, AWS, DigitalOcean, Fly.io or any modern cloud provider.

This guide will show you how to dockerize your NextJS app in minutes.

## Prerequisites

You will need the following installed on your machine:

- Docker Desktop
- NodeJS
- A package manager. I use pnpm as my package manager, but you can use npm or yarn. They have similar commands.

## Getting Started

You must configure your NextJS application to build a standalone application. This is required for running NextJS in a Docker container.

In your `next.config.js` file, add the following:

```
const nextConfig = {
  output: 'standalone',
  // ... other config
}
```

## Configuring Docker

### .dockerignore

Add a `.dockerignore` file to the root of your project. This file will contain the files and folders that you do not want to copy into the Docker container.

I say root of the project here because I build in a monorepo. But you can put this file in the root of your NextJS application if you’re not using a monorepo.

I use this `.dockerignore` file for my NextJS projects. You can use it as a starting point.

```
Dockerfile*
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
.jest_cache
.docker-compose
.vscode
.terraform
.husky
```

### Dockerfile

Add a `Dockerfile` to the root of your project. This file will contain the instructions for building your Docker image.

This is a multi-stage Dockerfile. This means that we will build the NextJS application in one stage and then copy the built files into a new stage. It results in a smaller final image size for deployment.

- STAGE 1: A container with pnpm and python3 is required
- STAGE 2: Fetch deps into the pnpm store
- STAGE 3: Copy the application code and install all deps from cache into the application
- STAGE 4: Build the NextJS app
- STAGE 5: Create a clean production image - only take pruned assets

Each stage is stored on the build machine’s cache. This means that if you make a change to your code, only the last 3 stages will be rebuilt. This is a huge time saver.

```
# STAGE 1: A container with pnpm and python3 is required
FROM node:18-alpine as pnpm_base

WORKDIR /app
# install pnpm
RUN npm i --global --no-update-notifier --no-fund pnpm@7
# install python3 and other deps
RUN apk add --no-cache g++ make py3-pip libc6-compat

# STAGE 2: fetch deps into the pnpm store
# We run pnpm fetch in a separate step to avoid re-fetching deps on every code change
# fetch is a pnpm command that downloads all dependencies to the local store
# You could remove or skip this step if using npm or yarn (but make sure to copy your lock file)
FROM pnpm_base as fetched_deps
WORKDIR /app
# setting production env usually speeds up install for your package manager
ENV NODE_ENV production
# copy the lock file that you use
COPY pnpm-lock.yaml ./
# set the store dir to a folder that is not in the project
RUN pnpm config set store-dir /workdir/.pnpm-store
RUN pnpm fetch

# STAGE 3: Copy the application code and install all deps from cache into the application
FROM fetched_deps as with_all_deps
# I use mono repo so I copy the whole project code (except for ignored things)
COPY . ./
# finally, install all the deps
RUN pnpm install --offline

# STAGE 4: Build the NextJS app
# Here we use pnpm filtering to only build the frontend app
# Then we use pnpm deploy command to prune the dependencies
FROM with_all_deps as builder
RUN pnpm --filter='*frontend' build
RUN pnpm --filter='*frontend' deploy pruned --prod

# STAGE 5: Create a clean production image - only take pruned assets
FROM node:18-alpine AS runner
WORKDIR /app
# We set the NODE_ENV to production to make sure that the NextJS app runs in production mode
ENV NODE_ENV=production
# We add a non-root user to run the app for security reasons
RUN addgroup --system --gid 1001 app
RUN adduser --system --uid 1001 app
USER app

# We copy the built NextJS app assets from the builder stage
# NextJS produces a backend server and a frontend app
COPY --chown=app:app --from=builder /app/apps/frontend/.next/standalone src/
COPY --chown=app:app --from=builder /app/apps/frontend/public src/apps/frontend/public
COPY --chown=app:app --from=builder /app/apps/frontend/.next/static src/apps/frontend/.next/static

# Set the port that the NextJS app will run on
# You should choose a port that is supported by your cloud provider
ENV PORT 5000
# Expose the port to the outside world
EXPOSE 5000

# Finally, we run the NextJS app
CMD ["node", "src/apps/frontend/server.js"]
```

## Building the Docker Image[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#building-the-docker-image)

Now that you have your Dockerfile, you can build the Docker image.

Here we build with a tag of `my-frontend-app`. You can name it whatever you want. But a named tag makes it easier to run the image later.

```
docker build -t my-frontend-app .
```

## Running the Docker Image[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#running-the-docker-image)

Now that you have your NextJS Docker image, you can run it.

```
docker run -p 5000:5000 my-frontend-app
```

This will run the NextJS app on port 5000. You can now access the NextJS app at `http://localhost:5000`.

## Conclusion

That’s it! You now have a NextJS app running in a Docker container.

You can now deploy this Docker image to any cloud provider that supports Containers (All of them do).

I describe how to run a node container of Dokku in this [blog post](https://www.darraghoriordan.com/2021/12/29/run-node-app-postgres-dokku-digital-ocean/).

If you have any questions, please leave a comment below.

If you want to see a real world example of NextJs in a Container, check out my [NextJS Starter](https://usemiller.dev/docs/miller-start/reference/miller-web/L0RvY2tlcmZpbGUtZmU=) project. It uses Docker to deploy NextJs to Dokku on [Digital Ocean](https://m.do.co/c/1ee4e460bc81).