Deploy Strapi on Kubernetes with HTTPS

Rajat Badjatya
The Startup
Published in
7 min readMay 24, 2020

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. 😉

Setup 🛠

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

Steps:

  1. Setting up the namespace and storage-class.
  2. Installing MongoDB for our Strapi project.
  3. Creating a docker image for Strapi with MongoDB and HTTPs configuration.
  4. Creating and deploying Kubernetes resources for Strapi.

Step 1: Setting up the namespace and storage-class

Namespace:

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:

Namespace Yaml
$ kubectl apply -f namespace.yml
namespace/my-es-cluster created
$ kubectl get namespaces
NAME STATUS AGE
default Active 50m
kube-system Active 50m
ns-strapi Active 1m

StorageClass

The next step is to define a new StorageClass for providing the persistent volumes for MongoDB and Strapi.

Each StorageClass contains the fields provisioner, parametersand 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.

StorageClass YAML

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.

Mongo YAML

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 3m
kubectl 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.

dockerfile

Most of the statements in above docker file are self-explanatory but I want to bring your attention to a few things:

  1. I have chosen node:11.1.0-alpine as a base image for our app.
  2. I have installed Nginx in our container and kept SSL certs to a path /etc/nginx/certs/ and nginx.conf to the path /etc/nginx/nginx.conf
  3. 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.

strapi.sh

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.

nginx.conf

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 namespace, storage classand animage 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 aPVC 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
pvc.yml

Specify the newly created PVC name in the below YAML. You might also need to change the value of these variablesDATABASE_CLIENT, DATABASE_HOST, DATABASE_PORT, DATABASE_NAME in the below YAML according to your setup.

strapi.yml

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 20m
masterstrapi-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 service.

📌Note: Two or three links including open the administration button 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.

Strapi accessible over browser

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.

--

--