Next Js with Minikube (Kubernetes)

Tariqul Islam
8 min readApr 10, 2023

In this Article, I will discuss about how the NextJS can be deployed into Minikube(Kubernetes) environment. I also discuss about the how to build the NextJS Application, How we can use multi stage layer build feature for Docker, How can we deploy the site with Ingress Controller. So later reader can use it as reference to deploy into any cloud service (Aws, Azure, gcloud) kubernetes environment.

Prerequisite

  1. Sample Next JS Application
  2. Docker Install into your PC (You can follow https://docs.docker.com/engine/install/)
  3. Docker Hub Account (You can follow https://hub.docker.com/signup)
  4. Minikube installed into your PC (You can follow https://minikube.sigs.k8s.io/docs/start/)
  5. Install Yarn (You can follow https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable)

Sample Application creation

> npx create-next-app@latest nextjs-minikube-integration
> cd nextjs-minikube-integration
> yarn dev

Build Docker Image

I use standaloneNextJS build mode and multi staging build for docker to build our docker image.

First enable the standalone build mode.

Open next.config.js file and add attribute (output:'standalone')to file

Run build command to create standalone package

> yarn build

Create Docker file into root of the application directory <app_root>/(Dockerfile)

# Start (Block 1) Dependency Solving Stage
FROM node:18-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else echo "Lock file not found." && exit 1; \
fi

# End (Block 1)

# Start (Block 2) Build the Application Stage

FROM node:18-alpine AS builder
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build

# End (Block 2)

# Start (Block 3) Create the Application Runner
FROM node:18-alpine AS runner
WORKDIR /app

# Declare the NODE_ENV
ENV NODE_ENV production

# Add the Group and User to Container
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy the next js application form builder image
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
# Expose the Application Port
EXPOSE 3000
# Add the Environment Port Variable
ENV PORT 3000

# Run the Application with "server.js" using node, which is
# Auto Generated by Standalone mode
CMD ["node", "server.js"]
# End (Block 3)

The following code for docker, I am using multi stage build to create the image and run the next js application with standalone mode using custom server in container.

Block 1, This layer using for solve all the dependency of the application, also check lock file is available into in application folder, because we are using the same application dependency, which we use at development time.

Block 2, This layer is using for building the next js application, it tooks the code source from block 1 (dependency layer), then copy the application path and build the next js application.

Block 3, This layer is using for run the application into container, setup the environment variable, create the group and user to run the application, copy the build source from Build layer (Block 2), then expose the port and run the application with custom server for Next JS application.

And then Run Docker Build command to create the docker image

> docker build -t tariqulislam/nextjs-kube-integration .

Check the Image is created successfully by following command

> docker images

Then run the following docker command to check the application is deployed into container successfully

# docker run -d  -p <hostport>:<containerport> image name
> docker run -d -p 3002:3000 tariqulislam/nextjs-kube-integration

Check on Web Browser application application in running on specific port

Publish and Push the image to Docker Hub

Log in to Docker Hub and Push the image to it

# <username> please check on your docker hub account
> docker login --username=<username>

# docker tag <IMAGE ID> <REPOSITORY NAME>
> docker tag 2abc11c4f67e tariqulislam/nextjs-kube-integration:latest

# Push the image
> docker push tariqulislam/nextjs-kube-integration:latest

Tagging the image is important before push the image to docker registry.

After running the push command docker is successfully pushed into database.

For Minikube Deployment, I will save the repository location info `docker.io/tariqulislam/nextjs-kube-integration`

Configure the minikube

Start Minikube in docker:

minikube start --driver=docker

To make docker the default driver:

minikube config set driver docker

Add Ingress addons

minikube addons enable ingress
kubectl get pods -n ingress-nginx
> minikube addons enable metrics-server

Minikube Dashboard Enable

> minikube dashboard
> minikube dashboard --url

Deploy the application to Minikube(Kubernetes)

apiVersion: apps/v1         #line 1  (required field) 
kind: Deployment #line 2 (required field)
metadata: #line 3 (required field)
name: nextjs-kube-app
labels:
app: nextjsweb
spec: #line7 (required field)
replicas: 3 #line8 How many Replica we want to create in cluster
strategy: #line9
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1 #line13
selector:
matchLabels:
app: nextjsweb #line16 matching with labels name of from metadata
template: #line17 Templeting the Deployment and declare the spec
metadata:
labels:
app: nextjsweb #line20 matching with label name of metadata
spec: #line21
containers:
- name: nextjsclient #line23
image: tariqulislam/nextjs-kube-integration:latest #line24
imagePullPolicy: IfNotPresent #line25
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 3000 #line31
restartPolicy: Always #line32

Important parts of Deployment file

apiVersion , metadata , kind , spec , are required filed for kubernetes deployment

apiVersion: delare which api version of kubernetes using for create the object

kind: which kind of kubernetes object we want to create

metadata: its helps to uniquely identify the object name in cluster after deployment

spec: using for control the state of object and provide desire configuration during deployment

line 9 to line 13, contains the deployment strategy of the application. line 17 to 32 is the template section where we will create the spec of container configuration and where to get the images to deploy the site.

#line23: provide the unique container name to kubernetes cluster

#line24: application image location, where to get the image and deploy to cluster

#line25: image pulling policy,

IfNotPresent: if image available in locally is will not pull from docker registry.

Never: it will not pull the image from remote registry, only using locally build image .

Always: it will pull the image always from remote repository

#line31, container port will be same as application web server running port for next js

Apply the deployment

> kubectl apply -f deployment.yaml
# Check the status of the pod
> kubectl get pod

Expose the service

apiVersion: v1
kind: Service #line2
metadata:
name: nextjs-kube-service #line4
spec:
selector:
app: nextjsweb #line7
ports:
- port: 3000 #line8
targetPort: 3000 #line9
type: NodePort #line10

Important Points of Service file

line2 Using the Service Kind to expose Service for deployed

line4 name of the service, which is be later identify the other services and other kind

line7, app name should be same as the label of deployment metadata label name, service will be discover the deployment by this name.

line8 portwhich port the service will be exposed to cluster

line9 targetPort,this will be application deployment port mapping, which port the application is deployed by deployment.yaml file.

line10, type , service type

clusterIP : it is default service type. its provide the load balanced IP address. one and more pods with same label selection can froward the traffice to specific IP address.

NodePort : it expose the port for each node. kube-proxy can use 30000- 42767 range of ports to expose the service.

LoadBalancer : create the service like ClusterIP and Open the Port to every node like NodePort.

So, For primary purpose of minikube, i just use the NodePort to expose the service for minikube.

Apply the service

> kubectl apply -f service.yaml
# check the Service is running
> kubectl get svc
# Get Service url from minikube by following command
> minikube service --urlo nextjs-kube-service

To get better routing access, functionality, I use the ingress to serve our application

Ingress for handling the application routing

First add the some fake domain into /etc/hosts file

*.next-kube.com
apiVersion: networking.k8s.io/v1 # Api Version
kind: Ingress #Service type: ingress
metadata: #line 3
name: ingress-nextjs-service
labels:
name: ingress-nextjs-service #line 6
annotations: #line7
nginx.ingress.kubernetes.io/rewrite-target: /$1 #line8
spec:
ingressClassName: nginx #line10
rules:
- host: "*.next-kube.com" #line12
http:
paths:
- path: /?(.*) #line15
pathType: Prefix #line16
backend: #line17
service:
name: nextjs-kube-service #line19
port:
number: 3000 #line21
  • Kubernetes ingress Nginx is used as a load balancer.
  • It can make some smarter routing decision based on the rules provided
  • Run the command manually on the Kubernetes master for deployments

line3: meta data which contains the service name and label to kuberntens can identity the service

line7 and line8, add the annotation so it will load with the nginx rules when server started, i add the extra configuration to rewrite the serving path of the next js application

line12, add the host name where to point out the service with virtual hosting

line15 and line16, how the http request are and page of the next js application will be serve

line 17 to 21, which service it will be mapped with, of this application it connects with nextjs-kube-service with 3000 port

Apply the ingress

> kubectl apply -f ingress.yaml 

# Get Ingress deployment service
> kubectl get ingress

Because we are using Minikube in docker so we have to tunnel it with host by following below command

> minikube tunnel

Try again with domain i added before, so we can see the output on this browser

All the codebase you can find into

And the Docker Hub Image you can find into

https://hub.docker.com/repository/docker/tariqulislam/nextjs-kube-integration/general

--

--

Tariqul Islam

Tariqul Islam have 9+ years of software development experience. He knows Python, Node Js and Java, C# , PHP.