Using Next.js 13+ App Directory with Hot-Reload Enabled in Docker — Simple Guide

In this everchanging world of software development, creating web applications that are both efficient and easily deployable across different environments is vital. Incorporating Docker with Next.js not only eases the deployment process but also ensures consistency across diverse environments.
However, when the Next.js 13 App Directory was released, I encountered challenges running the Next.js application in Docker with Hot-Reloading. After dedicating time to the issue, I found a solution detailed in this article.
Here are some highlighted benefits of leveraging Docker in this context:
- Consistency & Portability: Docker allows you to create a container that encapsulates the Next.js application and its dependencies, ensuring consistent behavior across different environments.
- Isolation & Reproducibility: Docker containers provide isolation, meaning that the application and its dependencies are packaged together, preventing conflicts with other software or dependencies on the host system.
- Version Control and Collaboration: Docker allows us to version-control the container configuration as a Dockerfile, making it easier for teams to collaborate, share, and maintain the Next.js application’s development and deployment environments.
and there are many more…
Let’s get started with our tutorial:
1) Initialize the New Next Application:
If you already have a Next application, skip this step! Create a new Next.js application using the following command:
npx create-next-app@latest
Select the appropriate options, an example would be as follows:

For further options visit the Official Website!
2) Install Docker:
If you don't have Docker already installed you can follow the guides:
For Windows: https://docs.docker.com/desktop/install/windows-install/
For Mac: https://docs.docker.com/desktop/install/mac-install/
For Linux: https://docs.docker.com/desktop/install/linux-install/
3) Create a Dockerfile:
Now, add a file at the root of the project with the name "Dockerfile"
and add the following code:
# Dockerfile
# **********
# base stage
# **********
FROM node:20.9.0-alpine AS base
WORKDIR /app
# **********
# deps stage
# **********
FROM base AS deps
# Copy package.json to /app
COPY package.json ./
# Copy available lock file
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
# Instal dependencies according to the lockfile
RUN if [ -f "pnpm-lock.yaml" ]; then \
npm install -g pnpm && \
pnpm install; \
elif [ -f "yarn.lock" ]; then \
npm install -g yarn && \
yarn install; \
elif [ -f "package-lock.json" ]; then \
npm install; \
else \
npm install; \
# If you want to throw error on lockfile not being available,
# uncomment the following lines
# echo "No Lockfile!"; \
# exit 1; \
fi
# Disable the telementary
ENV NEXT_TELEMETRY_DISABLED 1
# ***********
# inter stage
# ***********
FROM deps AS inter
# Copy all other files excluding the ones in .dockerignore
COPY . .
# exposing the port
EXPOSE 3000
# **********
# prod stage
# **********
FROM inter AS prod
RUN npm run build
CMD ["npm", "start"]
# **********
# dev stage
# **********
FROM inter AS dev
CMD ["npm", "run", "dev"]
In this, I have created five stages, and the following is the breakdown of each one:
1. Base Stage (base):
- Starts from the Node.js 20.9.0 Alpine image and sets the working directory to
/app
.
2. Dependencies Stage (deps):
- Inherits from the base stage.
- Copies
package.json
and any available lock files (yarn.lock
,package-lock.json
,pnpm-lock.yaml
) into the/app
directory. - Installs dependencies based on the detected lock file type (
yarn
,pnpm
, ornpm
). - Disables telemetry for Next.js by setting the environment variable
NEXT_TELEMETRY_DISABLED
to1
.
3. Intermediate Stage (inter):
- Inherits from the “deps” stage.
- Copies of all other files (excluding those mentioned in
.dockerignore
) into the/app
directory. - Exposes port 3000.
4. Production Stage (prod):
- Inherits from the “inter” stage.
- Builds the Next.js application by running
npm run build
. - Defines the default command to start the application with
npm start
.
5. Development Stage (dev):
- Inherits from the “inter” stage.
- Defines the default command to start the development environment with
npm run dev
.
4) Add Separate Docker Compose files for the Development and Production Environment:
Add the following code in docker-compose.dev.yaml
for the development environment:
# docker-compose.dev.yaml
version: "3.8"
services:
frontend:
container_name: frontend
build:
context: .
target: dev
restart: always
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
- WATCHPACK_POLLING=true
volumes:
- .:/app
- /app/node_modules
- /app/.next
ports:
- 3000:3000
Now, add the following code in docker-compose.prod.yaml
for the production environment:
# docker-compose.dev.yaml
version: "3.8"
services:
frontend:
container_name: frontend
build:
context: .
target: prod
restart: always
ports:
# this will run the build on port 80
- 80:3000
If you want to run the production build at port 3000, you can replace 80:3000
with 3000:3000
.
5) Run the Container:
Finally, to run the container, use the following commands:
# For Development:
docker-compose -f .\docker-compose.dev.yaml up
# For Production:
docker-compose -f .\docker-compose.prod.yaml up
And that’s it, Happy Coding! 😊
If you liked my story you can follow me for more interesting tips and tricks at Farasat Ali — Medium.
You can Find Me on LinkedIn and GitHub.
Also, Visit My Portfolio at:
You can also check out the related articles: