Strapi is an open source headless CMS that is simple to learn and use. Software developers can pair different frontend technologies with Strapi to have a production ready application within a few minutes after setup. With Strapi’s modularity, you can extend it with community maintained plugins. Furthermore you can override some of its default functionality to have a fully custom build.
In this tutorial, we will create a Strapi application and self host it on digital ocean. We will be using bitbucket which is a source code repository hosting service to deploy our project to a production environment. We will leverage a continuous integration and continuous delivery/continuous deployment process (CI/CD) service to trigger deployments when we push to our master branch.
Continuous integration is the process of combing several copies of source code to a common source. In git based version control terms, it can be described as merging serval branches of a project to a common branch. This process ensure that not only existing code is maintained, but also new code is be added.
Continuous deployment and continuous delivery can be jointly described as the process by which software is frequently and reliably released through and automated process using a deployment pipeline. A deployment pipeline is a system of autonomous processes that is designed to efficiently update code from a version control system to a production environment.
CI/CD enables us to keep our product up to date with the latest changes by providing us with a simple way of pushing updates straight to production. We could customise our pipeline to run tests before deploying changes. This design enables us to detect bugs before they reach production. This is beneficial especially for critical systems. Problems can be identified early before crippling or damaging the live system. Here are some of the CI/CD pipelines that leverage the advantages we have discussed: GitHub Actions, Jenkins, AWS CodePipeline, Kubernetes and Bitbucket pipeline.
Setting Up Strapi
To get started, we will set up our Strapi server instance. Strapi is highly dependent on node. Make sure you have it installed in your development machine. We will use the npx command which is a node package runner which will execute a script that will scaffold a new Strapi project in the terminal’s current working directory.
Open your terminal or command line prompt (cmd/terminal) and run the following command to create a scaffold of the Strapi server.
npx create-strapi-app@latest backend --quickstart
The command creates a bare bone Strapi system, fetches and installs necessary dependencies from the package.json file then initialises a SQLite Database. Other database management systems can be used using the following guide. Once every dependency have been installed, your default browser will open and render the admin registration page for Strapi. Fill in all the required fields to create your administrator account then you’ll be welcomed with the page below.
Strapi's content-type builder plugin allows us to define a schema that describes the structure and rules for organizing and validating data in the database. This guides the database on how to store our records. In this tutorial, we will be creating an API that returns a list of Movies and their actors. We will begin by creating a schema for movies and actors. By default, a user schema is generated. It contains the necessary fields to identify a user of the system.
This collection will be used to store a movie’s details.
Movie
for the Display name and click Continue.title
in the Name field.description
within the collection.poster
. This field will be used to save a movie’s poster. The poster type will be associated with uploads with image file extensions i.e .jpg and .png.This collection will be used to store varies actor names and will have a one to many relationship with the movie collection. Meaning that a movie can have many actors.
Actor
for the Display name and click Continue.first_name
in the Name field.last_name
within the collection.last_name
field. Select Relation
from the stack card of available collection types. Ensure that movie is selected on the relation dropdown. Use the image below as a guideBelow are the collection-type properties we have so far.
Authorization is the process of determining whether a user is allowed to access a resource. This process is often used in computer security to protect resources from unauthorised access. In our case the resources we are protecting is the collection types we have created. By default, no one can access the collection types immediately after creation, we need to allow actions and who can perform certain actions.
To get started follow those steps:
Settings
. It should be the last option on the panel.Roles
then the edit icon besides the Public option.Actor
and ensure that find
and findOne
options are checked as shown below.findOne
and find
are checked as shown below.We can now access a list of actors and movies without being authenticated. We did not select the other options since we do not want unauthorised users to make changes to our database. They should only be allowed to read from it.
We are going Digital Ocean and use their cloud infrastructure-as-a-service (IaaS) platform to create an Ubuntu instance (using the provider’s jargon, we will create droplet) where our database and Strapi application will live. A droplet is a private virtual machine (VM) instance that runs on virtualised hardware. The specs of the instance can be tweaked to your preference or according to the demands of your application.
Before we start, make sure that you have a Digital Ocean account and that you are logged in. To get started creating an instance, follow the steps below:
On the top navigation bar, hover over the create button to expose a list of available service. Click on droplets.
We will be redirect to the page below. You will be require to choose a station/region where your droplet will be located at. Choose the one which is nearest to your target market.
After selection your preferred datacenter region, you will be required to select the operating system that will power your droplet. The default is ubuntu and it is pretty sufficient for our project. There are also windows based and custom ISO instances just incase your Strapi application depends on something that runs best on a specific operating system.
You will then be required to select computing options based on your budget. The cheapest available configuration has 512MB of RAM, 1 CPU, 10GB SSD and a quota of 500GB in transfers bandwidth and costs about $4 per month. For our application, we are going to select a 2GB RAM, 2 CPUs, 60GB SSD and 3 TB transfer bandwidth option. This falls in line with the minimum required spec needed to deploy a Strapi application. You can find more information about deployment specifications on our documentation platform.
You will be required to select an authentication method. There are two available methods, using SSH Keys and Password Authentication. The SSH Key based method is the most secure one. SSH is the abbreviation of Secure Shell. It is a secure networking protocol that uses asymetric encryption while transporting data. It has many use cases some of which are documented in RFC4250, RFC4251, RFC4252, RFC4253 and RFC4254. The protocol relies on the client-server model having the server listening on Transmission Control Protocol (TCP) port 22.
We will use the SSH Key authentication method. You can generate one using the command below on your computer. If you are using a windows machine, you could use PuTTY.
1ssh-keygen
2# Display the Public Key that has been generated
3cat ~/.ssh/id_rsa.pub
Finally paste the public key on the textinput field and click the Add SSH Key
button. If you already have one there's no need of generating another one.
Once we have finished configuring our server instance (droplet), we will be greeted with the screen below.
We had setup ssh while configuring our droplet, we will now try it out by connecting to our vm instance. Open your command line and run the command below.
$ ssh root@your_server_ip
Replace your_server_ip
with the actual ip address of your instance. You ip address can be found on the top left of your analytic page.
Since we are using ssh on the server for the first time, our terminal with display the message below.
1 ECDSA key fingerprint is SHA256:YOuRsSHFi+nGeRp1nT0yTOB6d7x1GMgEx@mpl3wZ2BuMn5/I5Jvo.
2 Are you sure you want to continue connecting (yes/no)?
if you did not add SSH keys while setting up the droplet, you will be required to type the password you set.
Strapi depends on node to run therefore we must setup NodeJS on our server for our Strapi project to run. Use the command below to install NodeJS.
$ sudo apt-get install nodejs -y
After installing NodeJS we need to install the NPM Node.js package manager using the following command.
$ sudo apt install npm
Sudo is a command in Unix-like operating systems that allows users to execute commands with the security privileges of another user, usually the superuser (root). It's often used to perform administrative tasks that require elevated privileges.
We will be persisting data from our movie collection using Postgres Database management system. Remember on our local machine(development environment), the default database is SQLite. On the production environment, we will use Postgres because
Run the commands below to get started with the installation:
$ sudo apt install postgresql postgresql-contrib
$ sudo systemctl start postgresql.service
We can confirm whether Postgres is running in the background by running the following command:
sudo systemtctl status postgresql.service
Lastly we are going to create a database using those steps:
1. Connect to the DBMS using sudo -u postgres psql
2. Run the command CREATE DATABASE your_preferred_database_name;
3. View all databases with the command \l
. Once you are done you can exit the shell using the command quit
.
We are going to setup a firewall using Uncomplicated Firewall (UFW) . It is a unix utility tool and comes preinstalled in Ubuntu. Since it is disabled by default, we will enable it by running this command:
$ sudo ufw enable
We need to allow connections to the following ports 22 and 80. Port 22 is used by the ssh agent while port 80 will be used to serve HTTP requests. Use those commands to open the ports:
$ sudo ufw allow 22
$ sudo ufw allow 80/tcp
A reverse proxy is a server that is setup up to handle request before a web server. It can forward requests to other servers and send back responses to the client (browser) . It can be described as an intermediary server. Some of their uses include:
Examples of Reverse proxies are Nginx, Apache HTTP Server with mod_proxy, HAProxy and Microsoft Internet Information Service (IIS) with Application Request Routing. These options are among the best and are widely used in web hosting, content delivery networks and application deployments to improve scalability, security and performance.
We are going to use Nginx to port forward request to our Strapi application. We will bind port 80 to forward requests to localhost on port 1337 where our Strapi application will be running.
To install Nginx, use this command:
$ sudo apt update
$ sudo apt install nginx
We will add nginx to our firewall setup using:
$ sudo ufw allow 'Nginx HTTP'
We need to setup port forwarding on nginx. We will start by creating a back up of the current nginx configuration. We need the backup just incase we need a fresh starting point incase we mess up with the configurations.
1Create a backup within the same directory:
2$ sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak`
3# Open the current configuration file
4$ sudo nano /etc/nginx/sites-available/default
We are running the commands as a superuser please be carefully when using sudo. The nano command opens a command line text editor. Paste the code below and exit the text editor by pressing CTRL + X
1upstream strapi_upstream {
2 server 127.0.0.1:1337;
3 keepalive 64;
4}
5
6server {
7 listen 80;
8 server_name strapi_upstream;
9 root /var/www/html;
10
11 location / {
12 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
13 proxy_set_header Host $http_host;
14 proxy_set_header X-NginX-Proxy true;
15 proxy_http_version 1.1;
16 proxy_set_header Upgrade $http_upgrade;
17 proxy_set_header Connection "upgrade";
18 proxy_max_temp_file_size 0;
19 proxy_pass http://127.0.0.1:1337/;
20 proxy_redirect off;
21 proxy_read_timeout 240s;
22 }
23}
The code above defines a nginx server group named strapi_upstream. It points to a server on localhost running at port 1337. Next, we have a proxy pass server block that points to our Strapi instance. Now restart the nginx service to apply changes. We will begin by checking whether our configuration is okay by running this command:
nginx -t
The command will confirm that the configurations and valid if not it will raise an error. Now let's restart the service:
sudo systemctl restart nginx
To get started setting up the pipeline, we need to generate server keys and place the public key within our repository configurations. While running our pipeline, bitbucket will spin up a bare bone linux virtual machine (a runner) which will be able to access and run our source code. We will add our public key to our repository configurations to allow the runner to connect to our server via SSH. Follow the steps below:
ssh-keygen
in your terminal.cat ~/.ssh/id_rsa.pub
Repository Settings
.Add Key
. SSH Keys
. Click on the Copy public key
button. Append the key to the file .ssh/authorized_keys
. This will our the pipeline runner to ssh into our server and run scripts. Use the command below to append the key.
echo "ssh-rsa a_R3aLly_sTr1nG-fR0m_3iT3uCket=" >> ~/.ssh/authorized_keys
sshd_config
file.1# Should we allow Identity (SSH version 1) authentication?
2 RSAAuthentication yes
3
4 # Should we allow Pubkey (SSH version 2) authentication?
5 PubkeyAuthentication yes
6
7 # Where do we look for authorized public keys?
8# If it doesn't start with a slash, then it is
9# relative to the user's home directory
10AuthorizedKeysFile .ssh/authorized_keys
prod.sh
and will be responsible for pulling latest changes from our master branch, installing necessary dependencies and restarting the service that runs our application.1#!/bin/bash
2echo -e '\e[1m\e[34mPulling code from remote..\e[0m\n'
3git pull origin master
4echo -e '\e[1m\e[34m\nChecking for new npm version and installing..\e[0m\n'
5npm install -g npm
6echo -e '\e[1m\e[34m\nChecking for new pm2 version and installing..\e[0m\n'
7npm install pm2@latest -g
8echo -e '\e[1m\e[34m\nInstalling required packages..\e[0m\n'
9npm install
10echo -e '\e[1m\e[34m\nBuilding Strapi Instance..\e[0m\n'
11NODE_ENV=production npm run build
12echo -e '\e[1m\e[34mSwitching dir..\e[0m\n'
13cd ~
14echo -e '\e[1m\e[34m\nRestarting service..\e[0m\n'
15pm2 restart ecosystem.config.js
For our script to run automatically, we need to add a trigger. A trigger maybe a commit, branch, a pull request or a tag. In this tutorial we will use a branch based trigger. Everytime we push our code to the master branch or merge a pull request to the branch, our script will run. Therefore any time our master branch is updated, our server will pull the latest changes, build and deploy the changes. This process is done within a .yml file named bitbucket.pipeline.
1pipelines:
2 branches:
3 master:
4 - step:
5 name: Prod
6 script:
7 - echo "Deploying to production environment"
8 - ssh -v root@your_droplet_io "cd ~/repo_name && bash ./prod.sh"
git clone git@bitbucket.org:username/repository_name.git
cd repository_name && npm install
Before we continue, we need to setup our environment variables. Below is a sample environment file containing all the required variables required to spin up our application on the server. 1HOST=0.0.0.0
2PORT=1337
3APP_KEYS="toBeModified1,toBeModified2"
4API_TOKEN_SALT=tobemodified
5ADMIN_JWT_SECRET=tobemodified
6JWT_SECRET=tobemodified
7# Postgres Database stuff
8DATABASE_HOST=localhost
9DATABASE_PORT=5432
10DATABASE_NAME=strapi_prod
11DATABASE_USERNAME=postgres
12DATABASE_PASSWORD=postgres
Ensure that you set the appropriate credentials for your Postgres database. Using default username and passwords for Postgres is not advised. Use the example above only as a guide.
When we start our Strapi application and visit our ip address on our browser, we will be greeted by the page below. We should be able to access the application without specifying the port (1337) since we have a reverse proxy setup.
Every time we push our code to the master branch, our pipeline will run and deploy our latest changes to the production environment. We can view the pipeline’s progress using this link.
On clicking a specific pipeline, you will be able to track the steps the running is at. If the pipeline fails the last command and error will be displayed. Failed Pipelines send notifications via email. We can test our application in production by adding a new record to the Movie collection. You will have to create an admin user account similar to the development environment.
We can test out our api by going to this address http://you_ip_address/api/movies. The page should return a JSON object that contains details of the movies we create earlier.
The Strapi experience has always been about flexibility, customization, and open-source innovation. But we understand that managing infrastructure and scaling your application can sometimes be a daunting task, diverting your focus from what you do best: developing web experiences.
That's why we're excited to introduce Strapi Cloud, so you can leverage the same robust Strapi capabilities you love, but without the hassle of managing servers, worrying about uptime, or scaling infrastructure as your project grows. It will allow you to future-proof your apps with a CMS that meets the content management needs of all your stakeholders, no matter your use case, services or devices.
Strapi remains committed to open-source values, and self-hosting will always be a viable option. We believe in offering choices that align with your project's unique needs. Strapi Cloud is an additional resource for those who want to focus solely on development, leaving the infrastructure to us.
We are here to support you every step of the way, no matter which hosting option you choose. You can reach out to us and the community on our Discord if you have any questions!
In this tutorial we created a Strapi application, added collections and deployed to a server using CI/CD by utilizing bitbucket’s pipeline service. We setup a server on a cloud hosting service, installed Postgres and setup SSH. We learnt how to setup various Server authentication methods. We also walked through setting up a firewall and a reverse proxy that reroutes traffic to a specific port.