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:
$ cd strapi-project
Edit the .env file:
$ nano .env
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:
$ nano config/server.js
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:
$ npm install pg --save
Rebuild the application:
$ npm run build
Start the application in the background:
$ npm run start &
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:
$ nano Dockerfile
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:
$ nano .dockerignore
Add the following content:
node_modules/
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:
$ helm repo update
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:
$ nano 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:
$ kubectl get ingress
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:
$ kubectl get 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:
$ kubectl delete 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:


