7

I have a nextjs project that I wish to run using Docker and nginx.

I wish to use nginx that connects to nextjs behind the scenes (only nginx can talk to nextjs, user needs to talk to nginx to talk to nextjs).

Assuming it's standard nextjs project structure and the dockerfile content (provided below), Is there a way to use nginx in docker with nextjs?

I'm aware I can use Docker-compose. But I'd like to keep it under one docker image. Since I plan to push the image to heroku web hosting.

NOTE: I'm using Server Side Rendering

dockerfile

# Base on offical Node.js Alpine image
FROM node:latest as builder
# Set working directory
WORKDIR /usr/app
# install node-prune (https://github.com/tj/node-prune)
RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin
# Copy package.json and package-lock.json before other files
# Utilise Docker cache to save re-installing dependencies if unchanged
COPY package.json ./
COPY yarn.lock ./
# Install dependencies
RUN yarn install --frozen-lockfile
# Copy all files
COPY ./ ./
# Build app
RUN yarn build
# remove development dependencies
RUN yarn install --production
# run node prune. Reduce node_modules size
RUN /usr/local/bin/node-prune
####################################################### 
FROM node:alpine
WORKDIR /usr/app
# COPY package.json next.config.js .env* ./
# COPY --from=builder /usr/app/public ./public
COPY --from=builder /usr/app/.next ./.next
COPY --from=builder /usr/app/node_modules ./node_modules
EXPOSE 3000
CMD ["node_modules/.bin/next", "start"]

dockerfile inspired by https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile.multistage

Edit: nginx default.conf

upstream nextjs_upstream {
 server nextjs:3000;
 # We could add additional servers here for load-balancing
}
server {
 listen 80 default_server;
 server_name _;
 server_tokens off;
 proxy_http_version 1.1;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection 'upgrade';
 proxy_set_header Host $host;
 proxy_cache_bypass $http_upgrade;
 location / {
 proxy_pass http://nextjs_upstream;
 }
}
asked Dec 16, 2020 at 21:43
3
  • I don't know about nextjs, but if it does stuff on the server side and you want to hide it behind nginx, then let nextjs listen on localhost only on an internal port that you don't export from the docker image and let only nginx listen to the exported port and forwad to the internal port. See stackoverflow.com/questions/5009324/node-js-nginx-what-now?rq=1 Commented Dec 16, 2020 at 21:55
  • That's what I've looking at. Run NextJS at port 3000, have nginx port forward to NextJS. But I don't know how to do it in Docker. Commented Dec 16, 2020 at 22:45
  • Since in your example above nginx is running on port 80, you have to EXPOSE 80. Then when you run the container, you need to tell docker where to bind that port 80 to. F.ex. docker run -p 80:80 <image name> would tell docker to make the exposed port 80 (on to docker container) available at port 80 (on the host). Commented Dec 18, 2020 at 9:19

2 Answers 2

4

In order to be able to use Nginx and NextJS together in a single Docker container without using Docker-Compose, you need to use Supervisord

Supervisor is a client/server system that allows its users to control a number of processes on UNIX-like operating systems.

The issue wasn't nginx config or the dockerfile. It was running both nginx and nextjs when starting the container. Since I couldn't find a way to run both, using supervisord was the tool I needed.

The following will be needed for it to work

Dockerfile

# Base on offical Node.js Alpine image
FROM node:latest as builder
# Set working directory
WORKDIR /usr/app
# Copy package.json and package-lock.json before other files
# Utilise Docker cache to save re-installing dependencies if unchanged
COPY package.json ./
COPY yarn.lock ./
# Install dependencies
RUN yarn install --frozen-lockfile
# Copy all files
COPY ./ ./
# Build app
RUN yarn build
# remove development dependencies
RUN yarn install --production
####################################################### 
FROM nginx:alpine
WORKDIR /usr/app
RUN apk add nodejs-current npm supervisor
RUN mkdir mkdir -p /var/log/supervisor && mkdir -p /etc/supervisor/conf.d
# Remove any existing config files
RUN rm /etc/nginx/conf.d/*
# Copy nginx config files
# *.conf files in conf.d/ dir get included in main config
COPY ./.nginx/default.conf /etc/nginx/conf.d/
# COPY package.json next.config.js .env* ./
# COPY --from=builder /usr/app/public ./public
COPY --from=builder /usr/app/.next ./.next
COPY --from=builder /usr/app/node_modules ./node_modules
# supervisor base configuration
ADD supervisor.conf /etc/supervisor.conf
# replace $PORT in nginx config (provided by executior) and start supervisord (run nextjs and nginx)
CMD sed -i -e 's/$PORT/'"$PORT"'/g' /etc/nginx/conf.d/default.conf && \
 supervisord -c /etc/supervisor.conf

supervisor.conf

[supervisord]
nodaemon=true
[program:nextjs]
directory=/usr/app
command=node_modules/.bin/next start
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=true
[program:nginx]
command=nginx -g 'daemon off;'
killasgroup=true
stopasgroup=true
redirect_stderr=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=true

nginx config (default.conf)

upstream nextjs_upstream {
 server localhost:3000;
 # We could add additional servers here for load-balancing
}
server {
 listen $PORT default_server;
 server_name _;
 server_tokens off;
 proxy_http_version 1.1;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection 'upgrade';
 proxy_set_header Host $host;
 proxy_cache_bypass $http_upgrade;
 location / {
 proxy_pass http://nextjs_upstream;
 }
}

NOTE: using nginx as a reverse proxy. NextJS will be running on port 3000. The user won't be able to reach it directly. It has to go through nginx.


Building docker image

docker build -t nextjs-img -f ./dockerfile .

Running docker container

docker run --rm -e 'PORT=80' -p 8080:80 -d --name nextjs-img nextjs-img:latest

Go to localhost:8080

answered Dec 23, 2020 at 23:14
Sign up to request clarification or add additional context in comments.

1 Comment

When running supervisorctl stop nextjs, the child processes (processChild.js) were still running here. We had to use killasgroup+stopasgroup as for nextjs 13.4.19, too.
0

You can use docker-compose to run Nginx and your NextJS app in Docker container, then have a bridge network between those containers.

then in nginx conf:

server {
 listen 80;
 listen 443 ssl;
 server_name localhost [dns].com;
 ssl_certificate certs/cert.pem;
 ssl_certificate_key certs/cert.key;
 location / {
 proxy_pass http://nextApplication; // name based on your docker-compose file
 proxy_http_version 1.1;
 proxy_read_timeout 90;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection 'upgrade';
 proxy_set_header Host [dns].com;
 proxy_cache_bypass $http_upgrade;
 }
}

I didn't use upstream script, loadbalancer is on top of nginx (at the cloud provider level)

answered Dec 17, 2020 at 0:18

1 Comment

Is there a way to make it work without using docker compose?

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.