Deploying and Scaling Strapi on Kubernetes: Complete Guide

Strapi is a headless Content Management System (CMS) that simplifies the creation and administration of content for websites, web applications, and mobile platforms. Its user-friendly admin dashboard allows teams to add, update, or remove content without needing deep technical knowledge. In addition, Strapi lets you define custom content types and data structures, giving you maximum flexibility when organizing content. With seamless integration options, Strapi works smoothly with front-end frameworks, databases, and third-party services, making it a powerful solution for modern content management needs.

Strapi Flow Chart

Strapi is capable of handling large amounts of content and serving a growing user base efficiently, which makes it ideal for deployment on a Kubernetes cluster. Kubernetes provides scalability and supports multi-environment deployments. Furthermore, Strapi offers built-in role-based authentication and user permissions to ensure that only authorized users can access or modify content, adding a strong security layer for applications running in clusters.

Because it is designed for high-volume content and efficient scaling, Strapi pairs perfectly with Kubernetes. Kubernetes boosts Strapi’s scalability and supports deployments across different environments. With built-in role-based authentication and user permissions, Strapi ensures secure access to content in cluster-based applications.

This tutorial explains how to containerize, deploy, and scale a Strapi application on a Kubernetes Engine.

Prerequisites

Before getting started, ensure you have the following:

  • Access to a Kubernetes Engine Cluster.
  • A DNS A record pointing to the Ingress Load Balancer IP (this guide uses strapi.example.com; replace it with your own domain).
  • Access to a PostgreSQL database.
  • An Ubuntu 24.04 instance as a non-root sudo user to serve as the management workstation.
  • Docker installed on the workstation for containerization.
  • Kubectl installed on the management workstation to manage the Kubernetes Cluster.
  • Helm Package Manager installed on the workstation.
  • Your cluster Kube config file configured so kubectl can connect to the cluster.

Build a Strapi Application

Log in to your management workstation and add the Node.js repository:

$ curl -sL https://deb.nodesource.com/setup_22.x | sudo -E bash -

Install Node.js:

$ sudo apt install -y nodejs

Configure Git

Strapi uses Git for version control. If Git is not configured, set up your global username and email:

Set a global Git username:

$ git config --global user.name "Your Full Name"

Set a global Git email address:

$ git config --global user.email "email@example.com"

Create a new Strapi project with npx:

$ npx create-strapi-app@latest strapi-project --quickstart --js

This command generates a new Strapi application named strapi-project using the Quickstart option. It installs the latest create-strapi-app package, prepares the project structure, and installs all required dependencies.

After installation, press Ctrl + C to stop the server and continue with the configuration.

Containerize the Strapi Application

Next, configure Strapi to use a PostgreSQL database and prepare it for deployment on Kubernetes.

Navigate to the project directory:

Edit the .env file:

Update the database credentials inside .env:

DATABASE_CLIENT=postgres
DATABASE_HOST=your-postgres-hostname
DATABASE_PORT=5432
DATABASE_NAME=strapi_db
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_SSL=true
DATABASE_SSL_REJECT_UNAUTHORIZED=false
DATABASE_SCHEMA=public
DATABASE_CONNECTION_TIMEOUT=60000

Replace the host, port, username, database name, and password with your actual PostgreSQL credentials.

Back up the original config/server.js file:

$ mv config/server.js config/server.ORIG

Create a new config/server.js file:

Add the following configuration:

module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: env('STRAPI_URL'),
  app: {
    keys: env.array('APP_KEYS'),
  },
  webhooks: {
    populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false),
  },
});

Define the STRAPI_URL environment variable:

$ export STRAPI_URL="http://127.0.0.1:1337"

Install the PostgreSQL Node.js driver:

Rebuild the application:

Start the application in the background:

Open a browser and visit http://<SERVER-IP>:1337 to confirm the Strapi dashboard appears and create the first administrator account.

Create the Docker Image

Create a Dockerfile in the project directory:

Add the following instructions:

FROM node:22-alpine

RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev nasm bash vips-dev
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /opt/
COPY package.json package-lock.json ./
RUN npm install
ENV PATH=/opt/node_modules/.bin:$PATH

WORKDIR /opt/app
COPY . .
RUN chown -R node:node /opt/app
USER node
RUN ["npm", "run", "build"]

EXPOSE 1337

CMD ["npm", "run", "start"]

Create a .dockerignore file:

Add the following content:

Build the Docker image:

$ docker build -t strapi:latest .

Log in to DockerHub:

$ docker login -u <username> -p <password>

Tag the image with your DockerHub username:

$ docker tag strapi:latest <username>/strapi:latest

Push the image to DockerHub:

$ docker push <username>/strapi:latest

Prepare the Kubernetes Cluster

Before deploying the Strapi application, you must configure the Kubernetes cluster by installing the necessary plugins and creating essential resources. This section demonstrates the steps to install the Nginx Ingress Controller, the CertManager plugin, and to create a ClusterIssuer resource to issue Let’s Encrypt SSL certificates.

Follow the steps below to set up your cluster.

Install the Nginx Ingress Controller

Add the Nginx Ingress repository:

$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

Update Helm chart repositories:

Create a new ingress-nginx namespace for Nginx Ingress Controller:

$ kubectl create namespace ingress-nginx

Install the Nginx Ingress Controller:

$ helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx

After installation, a Load Balancer is provisioned automatically for your cluster. Verify that the assigned External-IP Address matches the Load Balancer IP in the Kubernetes Engine dashboard:

$ kubectl get services ingress-nginx-controller --namespace ingress-nginx

The Load Balancer may take several minutes to become ready. If the EXTERNAL-IP field shows as <pending>, wait a few minutes and try again. You can also verify the Load Balancer deployment by opening the cluster page in the Kubernetes portal and checking the linked resources.

Install the CertManager for SSL Certificates

Install CertManager:

$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml

Visit the official CertManager releases page to get the latest version.

Get the Load Balancer’s External-IP Address:

$ kubectl get services ingress-nginx-controller --namespace ingress-nginx

Add an A record for your domain strapi.example.com and point it to the External-IP Address of the Load Balancer.

Create a new manifest named clusterissuer.yaml:

Add the following configurations to the file:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: "hello@example.com"
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: nginx

Replace “hello@example.com” with your actual email address.

Apply the ClusterIssuer resource:

$ kubectl apply -f clusterissuer.yaml

Verify the ClusterIssuer deployment:

$ kubectl get clusterissuer letsencrypt-prod

A READY status of True confirms the successful deployment of the ClusterIssuer resource.

Deploy the Strapi Application

Create a new deployment manifest file strapi-deployment.yaml:

$ nano strapi-deployment.yaml

Add the following configurations to the file:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: strapi-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      name: strapi-app
  template:
    metadata:
      labels:
        name: strapi-app
    spec:
      imagePullSecrets:
        - name: regcred
      containers:
        - name: strapi
          image: /strapi:latest
          imagePullPolicy: Always
          command: ["sh", "-c", "npm run build && npm run start"]
          ports:
            - containerPort: 1337
          env:
            - name: STRAPI_URL
              value: "strapi.example.com"

This configuration creates a Deployment resource with two pods running the container image you built, labeled strapi-app. It uses the regcred secret resource to authenticate to DockerHub to pull the container image from a private repository. Replace the image <username>/strapi:latest with your actual repository and change strapi.example.com to your domain pointing to the cluster load balancer.

Apply the strapi-deployment.yaml deployment file to deploy the application:

$ kubectl apply -f strapi-deployment.yaml

Verify that the application is deployed:

$ kubectl get deployment strapi-deployment

Create the Service and Ingress

Create a new service manifest to expose the application:

$ nano strapi-service.yaml

Add the following configurations to the file:

apiVersion: v1
kind: Service
metadata:
  name: strapi-service
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 1337
  selector:
    name: strapi-app

Apply the Service:

$ kubectl apply -f strapi-service.yaml

Create an Ingress manifest:

$ nano strapi-ingress.yaml

Add the following configurations to the file:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: strapi-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - secretName: strapi-tls
      hosts:
        - strapi.example.com
  rules:
    - host: strapi.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: strapi-service
                port:
                  number: 80

The above Ingress resource allows external access to the strapi-service resource. It uses the cluster issuer resource letsencrypt-prod to issue a new SSL certificate for domain strapi.example.com and store it as a secret resource strapi-tls.

Apply the Ingress resource:

$ kubectl apply -f strapi-ingress.yaml

Verify the Ingress resource:

In a web browser, visit the following URL and verify that the login page displays over the HTTPS protocol:

https://strapi.example.com

Scale the Strapi Deployment

Scaling the Strapi application ensures it can efficiently handle high traffic loads without downtime or interruptions. In this section, you’ll scale the Strapi application by adjusting the number of running replicas.

Increase the number of running replicas:

$ kubectl scale deployment/strapi-deployment --replicas=4

The above command scales the deployment to 4 replicas.

Verify the deployment status:

$ kubectl get deployment strapi-deployment

Your output should be similar to the one below:

NAME          READY   UP-TO-DATE   AVAILABLE   AGE
strapi-deployment   4/4   4   4   67m

View the running pods:

Your output should be similar to the one below:

NAME                     READY   STATUS   RESTARTS   AGE
strapi-deployment-5d5ccdb8b5-gj96w  1/1   Running   0   10m
strapi-deployment-5d5ccdb8b5-l8wv7  1/1   Running   0   9m34s
strapi-deployment-5d5ccdb8b5-wcg2x  1/1   Running   0   100s
strapi-deployment-5d5ccdb8b5-xx8t6  1/1   Running   0   100s

Decrease the number of replicas:

$ kubectl scale deployment/strapi-deployment --replicas=2

Kubernetes automatically terminates the extra pods and adjusts traffic distribution.

You can test the fault tolerance by manually deleting a pod:

Replace <pod-name> with an existing pod name.

Kubernetes detects the change and automatically creates a new pod. The Ingress resource detects the new pod and starts routing requests without downtime.

Conclusion

You have created an example Strapi application using the Strapi API with a PostgreSQL configuration, then containerized, deployed, and scaled the application on a Kubernetes Engine. You also configured CertManager to issue Let’s Encrypt certificates and set up the Nginx Ingress controller to enable secure external access to the cluster services.

Strapi simplifies content management through an intuitive web interface, eliminating the need for technical expertise. This makes it a good choice for building low-code or no-code backends. For more information, visit the following resources:

Source: vultr.com

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: