Here is a small summary to deploy using Gitlab runner your NextJs application server by NGINX server.

  • Gitlab Runner: Deploy of our NextJs site hosted in Gitlab Repo(OnPremise/Gitlab.com)
  • NGINX: Server NextJs site static generated files

Dockerfile - It uses the node 14 image for building, then it copies the static files in the nginx image.
The Dockerfile could be simplified by performing the NextJs build directly on the Gitlab runner and then COPY the builded artifact within the nginx container.

# pull official base image
FROM node:14.19.0-alpine as build

# copy the package.json to install dependencies
COPY package.json package-lock.json ./

# Install the dependencies and make the folder
RUN npm install && mkdir /nextjs-ui && mv ./node_modules ./nextjs-ui

WORKDIR /nextjs-ui

COPY . .

# Build the project and copy the files
RUN npm run build


# NGINX Image Build
FROM nginx:1.17-alpine

## Remove default nginx index page
RUN rm -rf /usr/share/nginx/html/*

COPY --from=build /nextjs-ui/out /usr/share/nginx/html
COPY nginx/default.conf /etc/nginx/conf.d/default.conf


VOLUME /usr/share/nginx/html
VOLUME /etc/nginx

 # Don't run as root
USER 101

EXPOSE 8080

gitlab-ci.yml - Here we go through the docker image building process and docker stack deployment on our docker swarm cluster.

In my case am deploying on Docker Swarm, so I use the before_script section to configure the ssh configuration to deploy on the docker swarm manager node. Later the steps are very simple, we build our docker image and we push it to our Gitlab registry, then we deploy the docker stack or service.

before_script:
    - 'which ssh-agent || ( apk update && apk add openssh-client )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - touch ~/.ssh/known_hosts
    - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
  
stages:
     - build
     - deploy

build:
    cache: {}
    stage: build
    image: docker:git
    variables:
      DOCKER_DRIVER: overlay
    services:
      - docker:dind
    script:
      - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN ${CI_REGISTRY}
      - docker build --no-cache -t ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}:staging .
      - docker push ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}:staging
    environment:
      name: development
    rules:
      - if: $CI_COMMIT_BRANCH == "master"
  
deploy:
      stage: deploy
      variables:
        DOCKER_HOST: ssh://docker@docker-host
        SERVICE_NAME: my-nextjs-app
      image: docker:latest
      script:
        - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
        - docker stack deploy --with-registry-auth --compose-file=docker-compose.yml ${SERVICE_NAME}
      environment:
        name: development
        url: https://domain.example
      rules:
        - if: $CI_COMMIT_BRANCH == "master"

docker-compose.yml - This is a very simple compose file which take our previously builded image and deploy using Traefik labels.

version: '3'

services:
    web:
        image: "${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}:staging"
        ports:
            - "8080"
        networks:
            - traefik-net
        deploy:
            labels:
                - "traefik.enable=true"
                - "traefik.docker.network=traefik-net"
                - "traefik.http.routers.nextjs-router.rule=Host(`domain.example`)"
                - "traefik.http.routers.nextjs-router.entrypoints=https"
                - "traefik.http.services.nextjs-router.loadbalancer.server.port=8080"
                - "traefik.http.routers.nextjs-router.tls.certresolver=certbot"

networks:
  traefik-net:
    external: true

default.conf - this is our NGINX configuration, as you could see in our Dockerfile we COPY nginx configurations from the nginx/ folder in our repo to /etc/nginx/conf.d. This will tell nginx how to serve NextJs static files.

server {
    listen       8080;
    server_name  localhost;
    root   /usr/share/nginx/html;
   
    location ~ /.+ {
        try_files $uri $uri.html $uri =404;
    }

    location / {
        index  index.html index.htm;
    }


    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

With this configuration we can successfully deploy our NextJs site using Gitlab Runner, Docker and NGINX.