What is Strapi?
Strapi is a headless Content Management System. What does headless CMS mean? A headless CMS is a back-end only content management system (CMS) that makes content accessible via a RESTful API for display on any device.
Why Headless CMS?
An omnichannel experience of a product is the need of the hour. To deliver a seamless experience, you need an API-first headless CMS that can deliver content to multiple channels(Websites, Android Apps, iOS Apps, etc). To learn more about Strapi and headless CMS visit strapi.io
Though Strapi installation is quite easy on Linux and Windows host machine. But as all you know, Kubernetes adds a layer of abstraction which makes Strapi deployment somewhat effortful job. Also, Strapi doesn’t support HTTPs/SSL communication out of the box . So in this tutorial I will step by step walk you through the process of setting up and deploying production grade Strapi with HTTPs on Kubernetes.
As they say, let’s get started without any further delay. 😉
Prerequisites: A basic knowledge of Kubernetes along with access to a Kubernetes cluster. I recommend Google Kubernetes Engine (GKE), but the majority of this tutorial can be used on any Kubernetes cluster. In a pinch, you can also use minikube so you have a zero-cost way to follow along with me.
I have divided the whole deployment process into 4 steps to make it more understandable
- Setting up the namespace and storage-class.
- Installing MongoDB for our Strapi project.
- Creating a docker image for Strapi with MongoDB and HTTPs configuration.
- Creating and deploying Kubernetes resources for Strapi.
Step 1: Setting up the namespace and storage-class
By separating different projects in different namespaces, you can easily deploy as many Strapi projects. All you need to do is use a different namespace, and the new definitions will not overlap with the existing ones.
If you choose to use the
default namespace, then you can skip this step. Otherwise, you should apply the namespace file given below:
$ kubectl apply -f namespace.yml
$ kubectl get namespaces
NAME STATUS AGE
default Active 50m
kube-system Active 50m
ns-strapi Active 1m
The next step is to define a new StorageClass for providing the persistent volumes for MongoDB and Strapi.
Each StorageClass contains the fields
reclaimPolicy, which are used when a PersistentVolume belonging to the class needs to be dynamically provisioned. If you are using the GCP then volume provisioner would be
gce-pd. Alternatively, you can look for a more suitable option for StorageClass here.
Step 2: Installing MongoDB for our Strapi project
Strapi gives you the option to choose the most appropriate database for your project. It currently supports PostgreSQL, MongoDB, SQLite, MySQL, and MariaDB.
We will be setting up MongoDB for this tutorial and will be deploying it as a special Kubernetes resource known as
StatefulSet instead of normal
Deployment and the rationale behind that is “Containers are stateless!” they say, and “databases are pointless without state!”.
We want the volume with the mongo data to get attached again to a new Pod when the old
Pod vanishes. If we don’t do this, we will lose all the data in the database after any of
Pod goes down. The
StatefulSet controller will give the replacement
Pod the same name and access to the resources of the
Pod it replaced.
If you want to learn more about MongoDB installation on Kubernetes you can refer to this article from Kubernetes Team or if you are in a hurry then just replace the name of your
StorageClassand name of mongo
service in below YAML to make your MongoDB pods up and running.
The above YAML is pretty much the same as the YAML Kubernetes Team is using for MongoDB installation at this link. I just did 2 modifications, First, I added the pod’s affinity & anti-affinity to ensure the high availability of mongo pods these properties tell Kubernetes to schedule our pods to a different-different node that could be helpful in a case when a node gets crashed. Second, I added a security context to avoid permissions error and seamlessly access path
data/db inside the container.
If you are following me then after applying the above YAML, you should have three pods with a service created in your cluster. These correspond to the three nodes in your MongoDB set. You can see them with this command:
kubectl get pods -n ns-strapi
NAME READY STATUS RESTARTS AGE
strapi-mongo-0 1/1 Running 0 3m
strapi-mongo-1 1/1 Running 0 3m
strapi-mongo-2 1/1 Running 0 3mkubectl get svc -n ns-strapiNAME TYPE CLUSTER-IP EXTERNAL-IP PORT
mongo LoadBalancer 10.97.135.233 xxx.xx.xx.xx 30144:30129/TCP
Step 3: Setting up Strapi with MongoDB configuration.
Now comes the critical step of setting up the Strapi, let’s start by creating a docker file.
- Creating a docker file
To have more control in our hand I am creating a Strapi image from scratch instead of using community image.
Most of the statements in above docker file are self-explanatory but I want to bring your attention to a few things:
- I have chosen
node:11.1.0-alpineas a base image for our app.
- I have installed Nginx in our container and kept SSL certs to a path
/etc/nginx/certs/and nginx.conf to the path
- Gave read and execution permission to the startup script which we’ll be creating in the immediate next step.
- Creating a Startup script for Strapi image
We’ll use the shell script as the
ENTRYPOINT of a container and execute the necessary steps in order to set up the environment for our Strapi project.
With the help of the above script, we are not only configuring the
MongoDB required for Strapi but we are also handling the scenario in which our K8s pod got terminated and we want to start our Strapi project from the same stage where it was earlier. Also, we are kicking off Nginx using
nginx -g “daemon off;
- Nginx reverse proxy setting for redirecting HTTPs requests to HTTP
If you want to expose Strapi over HTTPs you need to apply some hack as native SSL support from Strapi was removed. A simple way to do so is by using the Nginx reverse proxy to listen to HTTPs traffic and redirect to Strapi running over HTTP.
If you want to learn more about Nginx reverse proxy you can refer to this link.
Now we have
dockerfile and startup script
strapi.sh ready, let’s create the docker image and push it to the Docker repository.
Build and push image:
sudo docker build -f dockerfile -t YOUR_REGISTRY:5000/strapi-image:v1 .sudo docker push YOUR_REGISTRY:5000/strapi-image:v1
Step 4: Creating and deploying Kubernetes resources for Strapi
If you played your cards right so far you could have a
storage classand an
image ready. If that’s the case, then you are only 2 steps away to make your Strapi project up and running on Kubernetes.
First, let’s use the sc-strapi a
storage class that we created in step-1 and create a
PVC which will help us to connect to the
Persistent Volume which will in turn connect to the underlying storage where we want to keep our Strapi project’s data.
kubectl apply -f pvc.yml -n ns-strapi
Specify the newly created PVC name in the below YAML. You might also need to change the value of these variables
DATABASE_NAME in the below YAML according to your setup.
After applying the above YAML you will see new pod(s) and service added in your namespace.
kubectl get pods -n ns-strapi
NAME READY STATUS RESTARTS AGE
strapi-mongo-0 1/1 Running 0 20m
strapi-mongo-1 1/1 Running 0 20m
strapi-mongo-2 1/1 Running 0 20mmasterstrapi-6c5bs 1/1 Running 0 3mkubectl get svc -n ns-strapiNAME TYPE CLUSTER-IP EXTERNAL-IP PORT
mongo LoadBalancer xx.xx.xx.xx xxx.xx.xx.xx 30144:30129/TCP
strapi LoadBalancer xx.xx.xx.xx xxx.xx.xx.xx 30143:31745/TCP
You can access the Strapi from your browser once your cloud provider controller configures the network load balancer and generate a stable IP address for Strapi
📌Note: Two or three links including
open the administrationbutton in Strapi UI panel will redirect you to
http://localhost:1337/which is happening because inside the container Strapi is still running on protocol HTTP and default port 1337. To access those links you need to copy the route and append it to your Strapi’s service load balancer IP.
To access the admin panel in the browser just copy the load balancer IP for Strapi service and append the route
/admin to it.
And, it brings us to the end of this article. I hope this article has enabled you to successfully deploy Strapi over HTTPs on Kubernetes. Feel free to comment out your thoughts and let me know if you face any issues in the implementation. 😊
Thank you Nitesh Ratnaparkhe and @Shubham Khatri for taking the time out to proofread this article and providing constructive feedback.