How to Deploy Strapi on a Kubernetes Cluster

Updated on 06 May, 2025
How to Deploy Strapi on a Kubernetes Cluster header image

Strapi is a headless Content Management System (CMS) that enables users to create and manage content across websites, web apps, and mobile platforms. It features an intuitive admin dashboard, enabling users to add, edit, or delete content without technical expertise. Strapi also allows users to define custom content types and data structures, offering greater flexibility in content organization. With seamless integration capabilities, Strapi integrates with front-end frameworks, databases, and third-party services, making it a versatile choice for modern content management.

strapi flow chart

Strapi is designed to handle large volumes of content and efficiently serve a growing user base, making it an ideal for deployment on a Kubernetes cluster. Kubernetes enhances Strapi’s scalability and supports deployments across multiple environments. Strapi also includes built-in role-based authentication and user permissions, ensuring that only authorized users can access or modify content — adding a layer of security for cluster-based applications.

Designed to handle high content volumes and scale efficiently, Strapi is well-suited for deployment on a Kubernetes cluster. Kubernetes enhances Strapi’s scalability and multi-environment support. With built-in role-based authentication and user permissions, Strapi also ensures secure content access within cluster-based applications.

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

Prerequisites

Before you begin, you should:

  • Have access to a Kubernetes Engine Cluster.
  • Create a new DNS A record pointing to the Ingress Load Balancer IP. This article uses strapi.example.com, replace all occurrences with your actual domain.
  • Have access to a PostgreSQL Database.
  • Have access to an Ubuntu 24.04 instance as a non-root sudo user to use as the management workstation.
  • Install Docker on your workstation for containerization.
  • Install Kubectl on your management workstation to manage the Kubernetes Cluster.
  • Install Helm Package Manager on your workstation.
  • Ensure you have your cluster Kube config file and have configured kubectl on your workstation to connect to the cluster.

Build a Strapi Application

  1. Access your management workstation and add the Node.js repository.

    console
    $ curl -sL https://deb.nodesource.com/setup_22.x | sudo -E bash -
    
  2. Install Node.js.

    console
    $ sudo apt install -y nodejs
    
  3. Configure Git.

    • Strapi uses Git for version control. If Git isn't configured, then configure your global username and email.

      1. Set a global username to use with Git.

        console
        $ git config --global user.name "Your Full Name"
        
      2. Set a global email address to use with Git.

        console
        $ git config --global user.email "email@example.com"
        
  4. Create a new Strapi project using npx.

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

    The above command creates a new Strapi application called strapi-project using the Quickstart installation type. It installs the latest version of the create-strapi-app package, sets up the project structure, and installs all necessary dependencies.

    Your output should be similar to the one below:

    Strapi   v5.11.0 🚀 Let's create your new project
    ...
    ...
    ? Please log in or sign up. Skip
    
     Strapi   Creating a new application at /home/linuxuser/strapi-project
    ...
     Actions available                                                            
    
    One more thing...
    Create your first administrator 💻 by going to the administration panel at:
    
    ┌─────────────────────────────┐
    │ http://localhost:1337/admin │
    └─────────────────────────────┘
    
    [2025-03-08 06:49:51.972] info: Strapi started successfully

    After the installation is complete, press Ctrl + C to stop the server and configure additional application settings.

Containerize the Strapi Application

In this section, configure your Strapi application to use a PostgreSQL Database, then set up an environment variable in the app's server file. Build the app's docker container image to deploy it on the Kubernetes cluster as described below.

  1. Navigate to the project directory.

    console
    $ cd strapi-project
    
  2. Edit the .env file.

    console
    $ nano .env
    
  3. Update the following values in the .env file.

    ini
    ...
    ## DATABASE CREDENTIALS
    DATABASE_CLIENT=postgres
    DATABASE_HOST=postgres.vultrdb.com
    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
    ...
    

    Save and close the file.

    The above configuration defines the database connection details for the PostgreSQL database. Replace postgres.vultrdb.com, 5432, user, strapi_db, and password with your actual database credentials.

  4. Back up the original config/server.js file.

    console
    $ mv config/server.js config/server.ORIG
    
  5. Re-create the config/server.js file.

    console
    $ nano config/server.js
    
  6. Add the following configurations to the file.

    javascript
    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),
      },
    });
    

    Save and close the file.

    The above code exports a configuration object that sets the host, port, url, and webhooks settings for the Strapi application. It retrieves values from environment variables.

  7. Define the STRAPI_URL environment variable.

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

    This above command sets the STRAPI_URL environment variable to the local Strapi server address and port.

  8. Install the PostgreSQL library for Node.js to enable the database connection.

    console
    $ npm install pg --save
    
  9. Rebuild the application.

    console
    $ npm run build
    

    The above command compiles the application, incorporating all recent changes.

  10. Start the application in the background.

    console
    $ npm run start &
    

    The above command starts the application in production mode. To verify that the Strapi application is running, visit the following address in your web browser and make sure Strapi web page displays. Replace <SERVER-IP> with your server’s actual IP address:

    http://<SERVER-IP>:1337

    The Strapi dashboard should appear, displaying the register-admin screen to create the first administrator account.

Create the Docker Image

  1. Create a Dockerfile in the project directory.

    console
    $ nano Dockerfile
    
  2. Add the following configurations to the file.

    dockerfile
    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"]
    

    Save and close the file.

    This Dockerfile:

    • Uses Node.js 22 Alpine as the base image.
    • Installs necessary dependencies and sets up the working directory.
    • Copies project files, builds the app, and exposes port 1337.
    • Starts the server with npm run start.
  3. Create a file named .dockerignore.

    console
    $ nano .dockerignore
    

    The .dockerignore file declares a list of files and directories to ignore while building the image.

  4. Add the following content to the file

    ini
    node_modules/
    

    This prevents the local node_modules/ folder from being copied into the image, letting Docker install dependencies fresh.

  5. Build the Docker image.

    console
    $ docker build -t strapi:latest .
    
  6. Log in to your DockerHub account.

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

    Replace <username> and <password> with your DockerHub username and password. To create a new DockerHub account, visit the DockerHub registry.

    Your output should be similar to the one below:

    ...
    Login Succeeded
  7. Tag the Docker image with your <username>.

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

    Replace <username> with your DockerHub username.

  8. Push the image to DockerHub.

    console
    $ docker push <username>/strapi:latest
    

    Replace <username> with your DockerHub username.

    Your output should be similar to the one below:

    The push refers to repository [docker.io/<username>/strapi]
    11f5e64c549b: Pushed 
    cb4435d821bf: Pushed 
    30eb464a5e79: Pushed
    ...

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, CertManager plugin, and creates a ClusterIssuer resource to issue Let’s Encrypt SSL certificates.

Follow the steps below to set up your cluster.

Install the Nginx Ingress Controller

  1. Add the Nginx Ingress repository.

    console
    $ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
    
  2. Update Helm chart repositories.

    console
    $ helm repo update
    
  3. Create a new ingress-nginx namespace for Nginx Ingress Controller.

    console
    $ kubectl create namespace ingress-nginx
    
  4. Install the Nginx Ingress Controller.

    console
    $ helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx
    
  5. 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.

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

    Your output should be similar to the one below:

    NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
    ingress-nginx-controller   LoadBalancer   10.100.115.77   192.0.2.1       80:32070/TCP,443:31590/TCP   6m8s
    Note
    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

  1. Install CertManager.

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

    Output:

    namespace/cert-manager created
    customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
    customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
    customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
    ...

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

  2. Get the Load Balancer’s External-IP Address.

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

    Your output should be similar to the one below:

    NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
    ingress-nginx-controller   LoadBalancer   10.100.115.77   192.0.2.1       80:32070/TCP,443:31590/TCP   6m8s
  3. Add an A record for your domain strapi.example.com and point it to the External-IP Address of the Load Balancer.

  4. Create a new manifest named clusterissuer.yaml.

    console
    $ nano clusterissuer.yaml
    
  5. Add the following configurations to the file.

    yaml
    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
    

    Save and close the file.

    The above configuration creates a ClusterIssuer resource to issue Let’s Encrypt certificates using the HTTP01 challenge. Replace "hello@example.com" with your actual email address.

  6. Apply the ClusterIssuer resource.

    console
    $ kubectl apply -f clusterissuer.yaml
    

    Output:

    clusterissuer.cert-manager.io/letsencrypt-prod created
  7. Verify the ClusterIssuer deployment.

    console
    $ kubectl get clusterissuer letsencrypt-prod
    

    Your output should be similar to the one below:

    NAME               READY   AGE
    letsencrypt-prod   True    57s

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

Deploy the Strapi Application

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

    console
    $ nano strapi-deployment.yaml
    
  2. Add the following configurations to the file.

    yaml
    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: <username>/strapi:latest
              imagePullPolicy: Always
              command: ["sh", "-c", "npm run build && npm run start"]
              ports:
                - containerPort: 1337
              env:
                - name: STRAPI_URL
                  value: "strapi.example.com"
    

    Save and close the file.

    The above configuration creates a Deployment resource:

    • It creates 2 initial pods with the container image you built, with the strapi-app label.
    • It fetches the credentials from regcred secret resource and authenticate to the DockerHub to pull the container image from private repository. Replace the image <username>/strapi:latest with your actual repository.
    • Replace strapi.example.com with your actual domain pointing to the cluster load balancer.
  3. Apply the strapi-deployment.yaml deployment file to deploy the application.

    console
    $ kubectl apply -f strapi-deployment.yaml
    

    Output:

    deployment.apps/strapi-deployment configured
  4. Verify that the application is deployed.

    console
    $ kubectl get deployment strapi-deployment
    

    Your output should be similar to the one below:

    NAME                READY   UP-TO-DATE   AVAILABLE   AGE
    strapi-deployment   2/2     2            2           19m
  5. Create a new service manifest to expose the application.

    console
    $ nano strapi-service.yaml
    
  6. Add the following configurations to the file.

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

    console
    $ kubectl apply -f strapi-service.yaml
    

    Output:

    service/strapi-service created
  8. Create an Ingress manifest.

    console
    $ nano strapi-ingress.yaml
    
  9. Add the following configurations to the file.

    yaml
    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
    

    Save and close the file.

    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.

  10. Apply the Ingress resource.

    console
    $ kubectl apply -f strapi-ingress.yaml
    

    Output:

    ingress.networking.k8s.io/strapi-ingress created
  11. Verify the Ingress resource.

    console
    $ kubectl get ingress
    

    Your output should be similar to the one below:

    NAME             CLASS   HOSTS               ADDRESS         PORTS     AGE
    strapi-ingress   nginx   strapi.example.com   192.0.2.1       80, 443   62m
  12. In a web browser, visit the below URL and verify that the log in page displays over HTTPS protocol.

    https://strapi.example.com

Scale the Strapi Deployment

Scaling the Strapi application makes it efficient to handle high traffic loads without downtime or interruptions In this section, you'll scale the Strapi application adjust the number of running replicas.

  1. Increase the number of running replicas.

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

    The above command scales the deployment to 4 replicas.

    Output:

    deployment.apps/strapi-deployment scaled
  2. Verify the deployment status.

    console
    $ 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
  3. View the running pods.

    console
    $ 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
  4. Decrease the number of replicas.

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

    Kubernetes automatically terminates the extra pods and adjusts traffic distribution.

  5. You can test the fault tolerance by manually deleting a pod.

    console
    $ kubectl delete pod <pod-name>
    

    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 an good choice for building low-code or no-code backends. For more information, visit the following resources.

Comments

No comments yet.