Deploying a Next.js app with Nginx using Docker


  • Linux
  • Docker
  • Node (optional, to test the application locally)
  • VS Code (or a text editor of your choice)


Why use a “real” web server

  • Performance benefits: building our app outputs minified and optimized code which will reduce the size of the application, reducing the load on the server.
  • Security: Nginx is “battle-tested” and it has HTTPS capabilities built-in with better performance.
  • Better tooling: Nginx provides logging, the ability to restrict, allow, or redirect server calls, load balancing, caching, and so much more.

Building our Next.js Application

content: [

Adding Prisma to the App

Setting up MongoDB inside a Docker Container

FROM mongo:4

# we take over the default & start mongo in replica set mode in a background task
ENTRYPOINT mongod --port $MONGO_REPLICA_PORT --replSet rs0 --bind_ip & MONGOD_PID=$!; \
# we prepare the replica set with a single node and prepare the root user config
INIT_REPL_CMD="rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '$MONGO_REPLICA_HOST:$MONGO_REPLICA_PORT' }] })"; \
INIT_USER_CMD="db.createUser({ user: '$MONGO_INITDB_ROOT_USERNAME', pwd: '$MONGO_INITDB_ROOT_PASSWORD', roles: [ 'root' ] })"; \
# we wait for the replica set to be ready and then submit the commands just above
until (mongo admin --port $MONGO_REPLICA_PORT --eval "$INIT_REPL_CMD && $INIT_USER_CMD"); do sleep 1; done; \
# we are done but we keep the container by waiting on signals from the mongo task
"prisma:format": "prisma format",
"prisma:generate": "prisma generate"

Setting up API Routes for Data Fetching

async function main() {
const intro = await{
data: {
name: "TOAA",
message: "Welcome to my little web app.",
const intro1 = await{
data: {
name: "TOAA",
message: "Leave a nice, little message for me",
console.log({ intro, intro1 });
"prisma": {
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"

Setting up Nginx

server {
listen 80;

location / {
root /usr/share/nginx/html/;
include /etc/nginx/mime.types;
try_files $uri $uri/ /index.html;
# using staged builds
FROM node:18-buster as builder
# make the directory where the project files will be stored
RUN mkdir -p /usr/src/next-nginx
# set it as the working directory so that we don't need to keep referencing it
WORKDIR /usr/src/next-nginx
# Copy the package.json file
COPY package.json package.json
# install project dependencies
RUN npm install
# copy project files
# make sure to set up .dockerignore to copy only necessary files
COPY . .
# run the build command which will build and export html files
RUN npx prisma db seed && npm run build

# bundle static assets with nginx
FROM nginx:1.21.0-alpine as production
ENV NODE_ENV production
# remove existing files from nginx directory
RUN rm -rf /usr/share/nginx/html/*
# copy built assets from 'builder' stage
COPY --from=builder /usr/src/next-nginx/out /usr/share/nginx/html
# add nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf
# expose port 80 for nginx
# start nginx
CMD ["nginx", "-g", "daemon off;"]

Service Startup with Docker Compose

version: "3.9"

# this service should use the web image after you build it
image: next-nginx:dev
- "3000:80"
NODE_ENV: development
# this service is the database service using mongo from docker hub
image: mongo-replica:latest
restart: always
- "27027:27017"




A Fullstack Engineer seeking truth, wisdom, and, above all, enlightenment where technology and philosophy intersect. | Fiction lover 🌐:

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Tawanda Eddie Jr.

A Fullstack Engineer seeking truth, wisdom, and, above all, enlightenment where technology and philosophy intersect. | Fiction lover 🌐: