Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Containerize and deploy Strapi applications on Kubernetes and Red Hat OpenShift

April 9, 2021
Joel Lord
Related topics:
ContainersKubernetesNode.js
Related products:
Red Hat OpenShift

Share:

    Strapi is the leading open-source headless content management system (CMS). It’s 100% JavaScript, fully customizable, and takes a developer-first approach. Strapi provides you with an interface to create and manage all the resources for your website. You can then build a front end to connect to your Strapi API with your favorite tools and frameworks. Content editors can use the friendly administration panel to manage and distribute content. Strapi is also based on a plugin system, which makes the CMS flexible and extensible.

    Once you've built your resources with Strapi's administration panel and designed a nice front end to serve the content, you will need to deploy the application somewhere. This article shows you how to deploy a Strapi application on a Kubernetes or Red Hat OpenShift cluster.

    Step 1: Set up the development environment

    To use Strapi in a containerized development environment, you will need three independent containers: One to run the database, another for Strapi, and one for the front end. This section shows you how to set up the three containers you will use in development.

    Initial setup

    The database and back-end servers must be able to communicate. You can use a Docker network for this communication. Create your network with the following command:

    $ docker network create strapi
    

    You will also need three folders to hold the data from your containers. Here is the command to create the /data, /app, and /front folders:

    $ mkdir ./data && mkdir ./app && mkdir ./front
    

    Create the database container

    To start a Strapi instance, you will need a database to persist your data. In this example, we'll use a MySQL database server running inside a container. This way, there is no need to go through the process of installing MySQL.

    To run the server, you can use the docker run command with the -d argument to run in the background. Include the following parameters:

    • --name to name the container.
    • -v to specify a folder to contain the data to reuse the next time you start the server.
    • -e to set up the environment variables to configure the database.

    The command to start the container should look like this:

    $ docker run --rm -d --name strapi-db -v $(pwd)/data:/var/lib/mysql:z --network=strapi -e MYSQL_DATABASE=strapi -e MYSQL_USER=strapi -e MYSQL_PASSWORD=strapi -e MYSQL_ROOT_PASSWORD=strapi-admin mysql:5.7
    

    Note that we use the --network parameter to connect the database container to the network we created earlier.

    After executing this command, try a docker ps to validate that the container has started.

    Create the back-end container

    Now that you've configured the database, you can start your strapi instance, which will run from a container. This time, you will use the strapi/strapi base image. You can still use the -d argument to run it in the background and --name to name your container. Be sure to also add the Strapi container to the same network as the database.

    You should also map your local /app folder to /srv/app:

    • Use the -v parameter so that you can persist the files created by Strapi using a local folder on your machine.
    • Map a port on your operating system to access port 1337 inside the container. If you are using port 8080, the address to connect to the Strapi admin console will be localhost:8080.
    • Configure Strapi to use the database you started in the previous step using environment variables.

    Here is the command to start the Strapi back-end container:

    $ docker run --rm -d --name strapi-dev -p 8080:1337 -v $(pwd)/app:/srv/app:z --network=strapi -e DATABASE_CLIENT=mysql -e DATABASE_NAME=strapi -e DATABASE_HOST=strapi-db -e DATABASE_PORT=3306 -e DATABASE_USERNAME=strapi -e DATABASE_PASSWORD=strapi strapi/strapi
    

    If Strapi can't find any files in the local file system that you mapped, it will automatically create a new instance of a Strapi server. This can take a few minutes. You can use docker logs to keep an eye on the application creation status:

    $ docker logs -f strapi-dev
    

    If you want to stop the logs in your console, enter Ctrl-C.

    Once you see a message stating that your Strapi server is started, you can go to http://localhost:8080/admin to create your admin user.

    After you've created the admin user, go ahead and create a new content type and make it publicly available. For content to work with in the next step, create a Content-Type for Posts. It will have four fields: title, author (a relationship to Users), publish_date, and content, as shown in Figure 1.

    The Posts content type has four fields: Title, Author, Publish_date, and Content.
    Figure 1: The new content type with four fields for posts.

    Note: See this video from the Strapi team for a full tutorial about creating new content types.

    Create the front-end container

    Next up, you will create a front end. This user interface (UI) will consist of a simple HTML file that fetches the Strapi application programming interface (API) data and displays it on the page.

    We'll use a Nginx server to display the content. You can start the container similarly to how you started the other two. This time, map port 80 in the container to port 8888 on your local machine and mount the /front folder to map to /usr/share/nginx/html inside your container. The /front folder is the default folder to serve files from with Nginx:

    $ docker run --rm -d --name strapi-front -p 8888:80 -v $(pwd)/front:/usr/share/nginx/html:z nginx:1.17
    

    Now, go ahead and create your front-end application. You might use a React, VueJS, or Angular application, but we'll use a simple HTML file for this demo. The file will do a fetch from the Strapi API to download the data and then create the necessary elements on the page using JavaScript.

    The HTML page will have a single div where the JavaScript code appends the API's content. Create the following index.html file in the /front folder:

    <body>
      <div id="content"></div>
    </body>
    

    You will need to add a script tag to include a configuration file, which will make it easier to overwrite your Strapi API location later. Add the following inside the index.html:

    <script type="text/javascript" src="config.js">
    

    The front/config.js file should create a global constant with the following configuration:

    const config = {
      BASE_URL: "http://localhost:8080"
    }
    

    Finally, in the index.html file, add another script tag that contains the code to download the content and display it on the page:

    window.addEventListener("DOMContentLoaded", e => {
      console.log("Loading content from Strapi");
    
      const BASE_URL = config.BASE_URL;
    
      const BLOG_POSTS_URL = `${BASE_URL}/posts`;
    
      fetch(BLOG_POSTS_URL).then(resp => resp.json()).then(posts => {
        for(let i = 0; i < posts.length; i++) {
          let postData = posts[i];
          let post = document.createElement("div");
          let title = document.createElement("h2");
          title.innerText = postData.title;
          let author = document.createElement("h3");
          author.innerText = `${postData.author.firstname} ${postData.author.lastname} -- ${postData.publish_date}`;
          let content = document.createElement("div");
          content.innerText = postData.content;
          post.appendChild(title);
          post.appendChild(author);
          post.appendChild(content);
          document.querySelector("#content").appendChild(post);
        }
      });
    });
    

    Now that you've created all the files go to http://localhost:8888 to see your application. You should see your fancy UI serving content from Strapi.

    Step 2: Set up the production environment

    When you are ready to deploy your application, you will need to create your own containers that contain all the necessary files and data. These containers will go live on the web.

    For each container, you will need to create a Dockerfile. You will use the Dockerfiles to create your containers with the actual content. Then, you'll deploy the containers to Kubernetes or OpenShift.

    Create the database container

    There is a good chance that you already have a database in production, and you probably won't want to overwrite its contents. For this reason, you will use the same default MySQL image that you used in development for the production database. If you want to import the SQL content later, you can use Docker to run a mysqldump command on your database:

    $ docker exec strapi-db /bin/bash -c 'mysqldump strapi -ustrapi -pstrapi' | tee strapi-db.sql
    

    This file will be imported into the production database later if it's needed.

    Note: The mysqldump command uses tee to copy the contents to a file. If you don't have the tee command, you can copy the docker command's output into a file named strapi-db.sql.

    Create the back-end container

    Next, you will create a Dockefile.back to build your container for the back end.

    Start from the strapi base image FROM strapi/base. Change the working directory to /opt/app and copy all the local files into the container. Next, expose port 1337 and set all your environment variables. Don't forget to add an environment variable for NODE_ENV=production. Finally, execute yarn build to build all the production resources and use the CMD command to start the back end once the container is started.

    Note: For more about using the Strapi base image, see the Strapi documentation on GitHub.

    FROM strapi/base
    WORKDIR /opt/app
    COPY ./app/package.json ./
    COPY ./app/yarn.lock ./
    RUN yarn install
    COPY ./app .
    ENV NODE_ENV production
    ENV DATABASE_CLIENT=mysql
    ENV DATABASE_NAME=strapi
    ENV DATABASE_HOST=strapi-db
    ENV DATABASE_PORT=3306
    ENV DATABASE_USERNAME=strapi
    ENV DATABASE_PASSWORD=strapi
    RUN yarn build
    EXPOSE 1337
    CMD ["yarn", "start"]
    

    Create the front-end container

    You'll have to do a bit of bash scripting to use an environment variable to specify your Strapi server's URL.

    Note: See my best practices for JavaScript front-end containers for more about using environment variables with front-end containers.

    First, start with the nginx:1.17 base image and change the working directory to /usr/share/nginx/html. In there, copy all the files from your local system into the container.

    The next step involves using sed to change the BASE_URL value to $BASE_URL. Then, you will pipe in the result to a new file called config.new.js and rename the file to config.js, overwriting the original.

    The result inside the container is a new config.js file that looks like the one below. Note that the original file in your local file system is left intact:

    const config = {
      BASE_URL: "$BASE_URL"
    }
    

    Finally, you will need to use envsubst to change the value of $BASE_URL to the environment variable's actual value. Make the following updates in the ENTRYPOINT, so the changes will happen when someone issues a Docker run:

    • Use a cat command to pipe the config.js file into envsubst.
    • Pipe the output to tee to create a new config.new.js file and rename the file to overwrite the previous one.
    • Use thenginx -g 'daemon off;' command to start the Nginx server:
      FROM nginx:1.17
      WORKDIR /usr/share/nginx/html
      COPY ./front/*.* ./
      RUN sed s/BASE_URL\:\ \"[a-zA-Z0-9:\/]*\"/BASE_URL\:\ \"\$BASE_URL\"/g config.js > config.new.js && mv config.new.js config.js
      ENTRYPOINT cat config.js |  envsubst | tee config.new.js && mv config.new.js config.js && nginx -g 'daemon off;'
      

    Updating the entry point instead of a RUN lets you specify different values for the base URL according to where the container is running.

    Build the containers

    Now that you have all your Dockerfiles ready, you can build the containers and push them to your favorite image registry. Don't forget to change the name of your images to use your username for that registry:

    $ docker build -t $DOCKER_USERNAME/strapi-front -f Dockerfile.front .
    $ docker build -t $DOCKER_USERNAME/strapi-back -f Dockerfile.back .
    $ docker push $DOCKER_USERNAME/strapi-front
    $ docker push $DOCKER_USERNAME/strapi-back
    

    Step 3: Package and run the application

    Now that you have containers with all of your code and data, you are ready to deploy the containers somewhere. We'll use Docker and Docker Compose to run the application and a Kubernetes or OpenShift cluster to deploy it.

    Package and run the application with Docker

    If you want to run this application, you can start all the containers in the same way you would in production.

    The commands to start the containers are similar to those you used in development mode, but with the mounted volumes and without the environment variables. We handled the source code and environment variables in the Dockerfile. Note that we add an environment variable specifying the location of the Strapi API for starting the front end:

    $ docker run --rm -d --name strapi-db -v $(pwd)/data:/var/lib/mysql:z --network=strapi -e MYSQL_DATABASE=strapi -e MYSQL_USER=strapi -e MYSQL_PASSWORD=strapi -e MYSQL_ROOT_PASSWORD=strapi-admin mysql:5.7
    $ docker run --rm -d --name strapi -p 1337:1337 --network=strapi $DOCKER_USERNAME/strapi-back
    $ docker run --rm -d --name strapi-front -p 8080:80 -e BASE_URL=http://localhost:1337 $DOCKER_USERNAME/strapi-front
    

    Package and run the application with Docker Compose

    If you want to share your application code and configurations with anyone else, you could provide them with a docker-compose.yaml file. This tool lets you manage multiple containers at once without multiple bash commands:

    version: '3'
    services:
      strapi-db:
        image: mysql:5.7
        volumes:
          - ./data:/var/lib/mysql
        networks:
          - strapi
      strapi-back:
        image: $DOCKER_USERNAME/strapi-back
        ports:
          - '1337:1337'
        networks:
          - strapi
      strapi-front:
        image: $DOCKER_USERNAME/strapi-front
        ports:
          - '8080:80'
        environment:
          BASE_URL: http://localhost:1337
    networks:
      strapi:
    

    Step 4: Deploy the application

    Once you've created all of your containers, you can deploy the application into a Kubernetes or OpenShift cluster. I'll show you how to do both.

    Deploy the application on Kubernetes

    Before deploying your application in a Kubernetes cluster, you will need to use YAML files to create all the necessary assets. For more details on each of these assets, see Kubernetes by example. To test out the deployment, you can use a smaller version of Kubernetes to run locally on your own machine. I've used Minikube for the following examples.

    Deploying the database

    The setup for persistent volumes (PVs) and persistent volume claims (PVCs) varies from one cloud provider to another. For this reason, the database in this example will not persist data. For more information about how to persist data, check your cloud provider's documentation.

    For the database, we will need to create a deployment. You will start by creating a YAML file that describes your deployment. You can give it a name, and in the spec, you will create a template for the pods. Each pod will have a single container, which will be the ones that you've pushed to your registry. Here is the deployment for this example (deploy-db.yaml):

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: strapi-db
    spec:
      selector:
        matchLabels:
          component: db
      template:
        metadata:
          labels:
            component: db
        spec:
          containers:
          - name: strapi-db
            image: mysql:5.7
            env:
              - name: MYSQL_DATABASE
                value: strapi
              - name: MYSQL_USER
                value: strapi
              - name: MYSQL_PASSWORD
                value: strapi
              - name: MYSQL_ROOT_PASSWORD
                value: strapi-admin
    

    Once you have your file, you can apply it to your cluster using kubectl:

    $ kubectl apply -f ./deploy-db.yaml
    

    Deploying the back end

    Your back end needs to be able to find the pods inside the cluster, so you will need to create a Service to expose each pod. We are using the defaults here, so you can use kubectl to create this service:

    $ kubectl expose deployment strapi-db --port 3306
    

    If you want to import data from your development environment SQL, you can run the following commands:

    $ kubectl cp ./strapi-db.sql $(kubectl get pod -l component=db | awk 'NR>1 {print $1}'):/tmp/strapi-db.sql
    $ kubectl exec -t $(kubectl get pod -l component=db | awk 'NR>1 {print $1}') -- /bin/bash -c 'mysql strapi -ustrapi -pstrapi < /tmp/strapi-db.sql'
    

    These commands copy the SQL file to the pods, then run a MySQL command to run it in the database.

    You can also create your deployments for the back- and front-end portions of your application. The Strapi back end (deploy-back.yaml) is the same as the database deployment, apart from the name, label, and container image:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: strapi-back
    spec:
      selector:
        matchLabels:
          app: strapi
          component: back
      template:
        metadata:
          labels:
            app: strapi
            component: back
        spec:
          containers:
          - name: strapi-back
            image: joellord/strapi-back
    

    Deploying the front end

    The front end (deploy-front.yaml) uses a similar structure to the back end, but you also need to set the environment variable for the back end's BASE_URL. For now, just set that variable's value to /api. You also need to expose the container to port 80 so that it will be available to the outside world eventually:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: strapi-front
    spec:
      selector:
        matchLabels:
          component: front
      template:
        metadata:
          labels:
            component: front
        spec:
          containers:
          - name: front
            image: joellord/strapi-front
            ports:
              - containerPort: 80
            env:
              - name: BASE_URL
                value: /api
    

    Create and expose the application services in your cluster

    Now that you've created your deployment files, you can apply them to your cluster and create the services for each one:

    $ kubectl apply -f ./deploy-back.yaml
    $ kubectl apply -f ./deploy-front.yaml
    $ kubectl expose deployment strapi-back --port 1337
    $ kubectl expose deployment strapi-front --port 80
    

    Everything is now running inside your cluster. You only need to expose the front- and back-end services to the outside world. For this, you will use an ingress.

    Here, you will create an ingress that exposes the front end as the default service. Any incoming request to your cluster then goes to the front end by default. You will also add a rule that redirects any traffic sent to  /api/* to the back-end service. The request will be rewritten when it's sent to that service to remove the /api part of the URL. We'll add a Nginx annotation in the metadata to effect this change. Here is the ingress.yaml file:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: main-ingress
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /$2
    spec:
      rules:
      - http:
          paths:
            - path: /api(/|$)(.*)
              pathType: Prefix
              back end:
                service:
                  name: strapi-back
                  port:
                    number: 1337
            - path: /()(.*)
              pathType: Prefix
              backend:
                service:
                  name: strapi-front
                  port:
                    number: 80
    

    Go ahead and apply this file to your cluster. You might need to enable the following add-on if you are using Minikube and have never used an ingress before:

    # For minikube users
    $ minikube addons enable ingress
    
    $ kubectl apply -f ./ingress.yaml
    

    You now have everything needed to run your Strapi application in a Kubernetes cluster. Point your browser to the cluster URL, and you should see the full application running in your cluster. If you're using Minikube, you can use the command minikube ip to get your cluster's address.

    Deploy the application on OpenShift

    Deploying the application on OpenShift can be even easier than deploying in a Kubernetes cluster.

    In this case, you can test out your deployment with the Developer Sandbox, which gives you access to an OpenShift cluster for free for 14 days.

    Create the deployment from an image

    The command-line interface (CLI) tool that you use to manage your cluster (oc) can create a deployment directly from an image. To deploy your application, enter:

    $ oc new-app mysql:5.7 MYSQL_USER=strapi MYSQL_PASSWORD=strapi MYSQL_DATABASE=strapi -l component=db --name strapi-db
    $ oc new-app joellord/strapi-back-openshift --name strapi-back
    $ oc new-app joellord/strapi-front-openshift --name strapi-front
    

    Note: Images on OpenShift need to be run as a non-root user. See my guide to front-end best practices for more about non-root images. The Dockerfiles used for this project can be found in the Git repository for this article under Dockerfile.rootless.back and Dockerfile.rootless.front.

    Seed your database with the data that you exported earlier. This data should be in your current working directory and have the name strapi-db.sql.
    $ oc exec -it $(oc get pods -l component=db | awk 'NR>1 {print $1}') -c strapi-db -- bash -c 'mysql -ustrapi -pstrapi strapi' < ./strapi-db.sql

    Expose the application

    Next, you'll want to expose the application to the outside world. OpenShift has a neat object for this purpose, Route, which you can use from the OpenShift CLI. Use the oc expose command to expose the back- and front-end to the outside world:

    $ oc expose service strapi-back
    $ oc expose service strapi-front --port=8080
    

    Now that your back end is exposed, you will need to set your front-end environment variable to the back-end route. Start by getting the public route for the Strapi API:

    $ oc get routes
    

    You should see all the routes that you've created so far. You can store the back-end route in a variable and then set it as an environment variable using oc set env:

    $ export BACKEND_ROUTE=$(oc get routes | grep strapi-back | awk '{print $2}')
    $ oc set env deployment/strapi-front BASE_URL=http://$BACKEND_ROUTE
    

    You can now access your Strapi application using the route for the strapi-front service.

    Summary

    When you are ready to put your Strapi application in production, the first step will be to containerize your whole setup. Once you have that done, you can deploy those containers in Kubernetes. You've also seen how easy it is to deploy a Strapi application to OpenShift.

    Last updated: May 31, 2023

    Recent Posts

    • More Essential AI tutorials for Node.js Developers

    • How to run a fraud detection AI model on RHEL CVMs

    • How we use software provenance at Red Hat

    • Alternatives to creating bootc images from scratch

    • How to update OpenStack Services on OpenShift

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue