Build a website with Gatsby and Strapi - Part 1: 12 steps to install and deploy Strapi

May 8th, 2020 - ☕️ 15 min read

For the first part of this tutorial series, we will see how to install, configure, and deploy Strapi.

Info: This tutorial series is also available on my website

If you have any problems or struggle with this tutorial, let me know in the comment section (I check the comments very often)

Also, let me know what you think of this tutorial and if there is the stuff that can be added.

As said in the introduction article, Strapi is a powerful headless CMS that allows a lot of flexibility.

In this tutorial, we will set both a development and production environment.

  • Production: on Heroku with a PostgreSQL database
  • Development: on our local machine with the standard sqlite3 database

Step 1 - Install Strapi on our machine

Be sure to match all the requirements before continuing.

- Node.js - 12.X minimum
- npm - 6.X minimum

Our strapi project will be named "strapi-cms"

To create it, open a new terminal window and type the following line :

yarn create strapi-app strapi-cms --quickstart

Strapi is going to install all required dependencies, setup all needed files, and a sqlite3 database for our local environment.

Step 2 - Create an admin user

Now that Strapi is installed, a browser window will open. If it does not open, navigate to localhost:1337/admin and here is what you should see :


We are now going to fill the form and click the "Ready to start" button.

Warning : This user will only be saved in our local sqlite3 database, we willl have to recreate it in production

Step 3 - Create our first collection type for our articles

Strapi works with objects named "Collection types". If you have already worked with an MVC structure, a collection type is the equivalent of a model.

In a collection we define :

* Name
* Fields
* Relations with other collection types

To create one, let's take a look to the left sidebar


Click on Content-Types Builder


You can see that we already have 3 collection types :

  • Permission
  • Role
  • User

They have been generated by strapi when we have created the project.

Now we are going to click on Create new collection type.

The display name of this new CT will be an article.


We are asked to select the different fields of this CT.


This is the list of the different fields we need with their names and types. After adding a field, click on Add another field until you reach the end of the list.

For our article we will select :

  • Text for the title → Short text named title
  • The date for the publication date → Date named date
  • Text for the description → Long text named description
  • Media for a cover picture → Single media named cover
  • Rich Text for the content → named content

We have now created our article collection type. Let's now hit the save button on the top right corner of the page.

What have we done?

If we think in an MVC way, we have set up our database and our models. We have specified that we have a table named "article" in which we will find different fields called: title, date, description, cover, and content.

This model is directly saved in our project as a JSON file (Which we will see a little bit later)

Step 4 - Create other collection types

We already have an article CT. But we need to add some extra fields.

As we are building a blog, it would be awesome to also have tags on each of our articles. It will allow us to create a search function and list every article of a specified tag.

Let's add a CT named tag. To do that, just follow the steps of the article CT creation and specify these fields:

  • Text for the name → Short text named name

That's all. We don't need anything else.

As this CT is created, let's link it to our article CT so we will be able to add tags to each article.

Go on the article CT and click Add another field then add a relation field and on the right side, select the Tag CT.

Let's speak about cardinality. If you ever worked with databases and created one, you will know that cardinality are very important. They allow us to know how much A have and/or belongs to B. Thanks to this result, the database creator will know if he has to create a new table or just a simple field.

In our case Articles has and belongs to many Tags, We have a cardinality of 1-1 (or - ) so we have to create a table, which is what we have done creating a new CT.

As I said, Articles has and belongs to many Tags. We now have to specify it by selecting this icon:


Click Finish, then Save and we are all set.

We will certainly add other fields or CT during this tutorial serie. When it will be the case, I will give you the list of fields and types you have to create.

Step 5 - Create an app on Heroku

Our Strapi app is now all set but we can access it only via localhost. We now have to put this app in production to access it from anywhere.

We will use Heroku. which is a Cloud application platform that allows you to host, deploy, and serve web-apps.

You can get a free dyno here (not a sponsored link)

I have free a Hobby dev thanks to Github education pack.

Once your account is created, we will add a new app. For me, this app is called tsflorus-strapi-tutorial and I have chosen Europe for the region as I am in France.

Our Heroku app is now created.


Step 6 - Add the Postgres add-on to our Heroku app

As said at the beginning of this tutorial, we need to set up a database along with our strapi instance.


As Heroku is a cloud platform, when you will not be using your dyno, it will go asleep. And every-time the dyno will start, you will lose your data including articles, tags, etc.

We will be using a PostgreSQL database with the Heroku-Postgres add-on.

To add this add-on, click on Ressources on the top navbar then search for Postgres in the Add-ons section.


Click on the right add-on then select your plan. For this tutorial, we will go one a Hobby Dev plan.


Step 7 - Add our database information in the config vars

To secure our Strapi app, we will not put our database information directly in our config file but we will use environment variables.

To set environment variables inside of our Heroku app, go to Settings on the top navbar.


Let's click on Reveal Config Vars

We now see a var called Database URL and its value is a very long string such as this one:


This URL contains every information of our database, Name, Username, Password, Host, Port.

In this case, we have :

  • DATABASE_NAME = da2n7sjsnqblr5
  • DATABASE_USERNAME = fisojprjbmfmpl
  • DATABASE_PORT = 5432
  • DATABASE_PASSWORD = e94f1e76a464632410d8246b47eca1e3f0f78e817165c9a7e5a22013f5499025

For each info, we have to create a new var. Once its done, your page will look like this.



Step 8 - Configure your strapi database connection

The last step for setting up the database is to update the strapi configuration.

Let's open your favorite code editor on the root directory of your strapi project and go to config/environments/production/database.json

Make modifications to have exactly this file :

      "defaultConnection": "default",
      "connections": {
        "default": {
          "connector": "bookshelf",
          "settings": {
            "client": "postgres",
            "host": "${process.env.DATABASE_HOST}",
            "port": "${process.env.DATABASE_PORT}",
            "database": "${process.env.DATABASE_NAME}",
            "username": "${process.env.DATABASE_USERNAME}",
            "password": "${process.env.DATABASE_PASSWORD}",
          "options": {}

What we are telling to strapi here is to use our environment variables to get the database information. It will use the few vars we have created on the last step.

Step 9 - Deploy your strapi app on Heroku

Deploy an app to Heroku is very easy. There are a few ways to do it.

  • Github hook → The app will rebuild every-time you update the default branch (master for example)
  • Container registry → If you are using Docker you will be able to deploy your Docker-based app to Heroku from its CLI
  • Heroku Git → Deploy an app directly with CLI by making it a Heroku repository

In our case, we will use the third method.

Heroku git and github can be used in parallel.

First of all, let's add a line to the .gitignore file.


If you don't add package-lock.json in the gitignore file, it may cause problems on Heroku.

Now, we will need to install a package named pg which is a Postgres package. If you forgot to install it, your build will be marked as succeeded but you will get an error when opening the app


If it was your case, just install pg package and redeploy your app.

If you have already install pg go on your app with terminal and type

heroku logs --tail

That will tell you where the problem is.

To install pg open a terminal window on the root directory of your Strapi project and enter the following command:

npm install pg --save

To deploy our application we need to go to the root folder of our Strapi app and follow these steps :

1 - Login with Heroku

heroku login

This will open a webpage so you can log in with your Heroku credentials

2 - Initialize the git repository

If you are using an existing git repository, skip this step

git init

3 - Set the remote branch for our app (here, tsflorus-strapi-tutorial has to be replaced with your Heroku app's name)

heroku git:remote -a tsflorus-strapi-tutorial

4 - Add all of our changes

git add .

5 - Make a commit message

git commit -m "Initialize my Strapi app"

6 - Then, push to the master branch

git push heroku master

Heroku will now build your Strapi app into your Dyno. You can see the build logs by clicking on Overview in the top navbar.

Once the build is noted as succeeded you will be able to see your app by clicking the Open app button on the top-right corner of your screen.

And you will see that your app is now running in production!


We will now need to recreate an admin user. To do that, go on replacing your project with your app name.

Exactly as we have done in the beginning, complete the admin user creation form then log in.


Congrats! You now have an instance of strapi running in production.

Keep in mind that every-time you will make changes in development mode (locally) you will need to redeploy your strapi app to see them production. Plus, the entry you will add on development mode will not be added in the production mode as we are not using the same database. If you want, you can setup the same database for both dev and prod environments.

Step 10 - Create our first tag and article

We are now going to create our first tag and article.

Let's click on the Tags CT on the left navbar, then click on Add New Tag. **** Our first tag will be named hello-world. Enter this name when you are asked to and click save.

Now that we have our tag, let's add an article.

Click on Articles on the left navbar and you should see this window:


Add the content of your choice for each field and save your new entry.

The content field is a markdown editor. If you don't now about markdown yet, check this link


Now, click on save and your article is saved!

Step 11 - Adjust permissions

As a headless CMS, Strapi renders content via an API (I will upload very soon an article on APIs). For short, it means that we will not fetch data with a database connection but directly via a URL.

By default, Strapi's API is secure so that anyone cannot query our data. For this tutorial, we will allow every public user to query data of our Strapi API.

!! Disclaimer !! Never do this for a real serious project! We are going to remove user protection just for the needs of this tutorial. Always secure your APIs!

On the left navbar of Strapi's dashboard, click on Roles & Permissions.

You can see that we have two different user roles. An Authenticated and a Public user.

Both roles are empty of user because we haven't add one. Your admin user isn't in the user table but just allow you to access this Admin Dashboard


We will edit permissions for the Public user.

Once you have clicked on Public, you will see under Permissions our two CT: Article and Tag.

For both of them, we are going to check find and find one checkbox.

Note that on the right side you see a URL. In this case it is /tags/:id.This is the path we will have to use to fetch a specified tag.


We will also need to set this find and find one permissions for the Users-Permissions on the bottom of the page


Let's click on save.

Now we are ready to fetch our first article!

Step 12 - Fetch our first article

The default route for fetching your collection-types will be where your project is the name of your app and collection-type the name of the CT you want to fetch.

You can fetch this data from anywhere just entering this URL. Personally, when I work with APIs I prefer to use a software named Insomnia which is a powerful tool for REST APIs.

If I fetch the CT articles, we will have this response:

Always add an extra "s" at the end of your CT name in the URL. To fetch our Article CT, I have to enter this URL :

        "id": 1,
        "title": "Hello world",
        "date": "2020-05-10",
        "description": "Just want to say hello to the entire world",
        "content": "# Oppugnant deus ficta fulva oculis manent lupis\n\n## Undis meminisse tum pariterque uterque iras lupum\n\nLorem markdownum Antiphatae moto, sis nuper, habuit coniunx, rursus. Volvens\nexamina, enim Medea, non stant me vocis ignarus! **Rector** aras omnia possis\ndomo querellis nullis, per verba, est ponti militia.\n\nPosset inquit. Vero ferarum offensi umorque; **ille Cereri**, vertitur hoc.\nVitalesque superba, inde suos **di iuxta**. *Ipsa formam*: agnus nec fluentum\ndextera Thetidis et Aurora pedumque: et.\n\n1. Et pulsa fovit me decorem sonantia strigis\n2. Succedit pastor\n3. Furta poteras est pulsus lenis Helenum origine\n4. Celebratior ferunt gravi\n5. Succurritis miseram opera spectacula aris violata signum\n6. Quem nudae genu vulnus\n\n## Muris quisquis\n\nTraiecit iactarique; eadem si pererrat orbis, hinc victor est fusum digitos\nbipenni ad altismunera. Pectora est placent, qui muris, ille nexu nomenque\nfidas, portus tonsa parentis, et nec.\n\n1. Ignorat verum illa videt volatu puer\n2. Lupi fugientis fures\n3. Sub octonis volitare herbas natasque opacas\n4. Silva Poeantia fueramque grande confluat\n5. Quae sit eodem circum audiat quid\n\nGramina Achaica multaque tepebat pede: iusserat, et esse, miserrima agebat\nstirpe, fateor lumina tabulas! Mortisque tigres sopistis Arethusae novit\n**turbida**: at Latiis vellet morsibus: *flamma*.\n[E]( Peleus pompas spectans erigitur\npenetratque tremensque parce. Vellera omnes; ulla absit sustinuit corque, denos\ncum templa Placatus animi.\n\n- Mea ensem adnuit\n- Aspicit ora Lucina\n- In equi florent\n\nDigna intrarant o vindice faciem, deinde, me nomen! Me fata quodque, iunxisse\nstrictique maximus et cantus ecquem vestrumque reliquit membra cum mediis omnia,\nadmissi? Medio arvum timuit obsequio aperti! Aspera pendeat ausus ad declinat\nter adhuc si cibis venabula *meos ibi*.",
        "created_at": "2020-05-10T14:52:24.883Z",
        "updated_at": "2020-05-10T14:52:24.883Z",
        "cover": {
          "id": 1,
          "name": "bermuda-searching",
          "alternativeText": "",
          "caption": "",
          "width": 876,
          "height": 912,
          "formats": {
            "small": {
              "ext": ".png",
              "url": "/uploads/small_bermuda-searching_ff94e074d2.png",
              "hash": "small_bermuda-searching_ff94e074d2",
              "mime": "image/png",
              "path": null,
              "size": 110.39,
              "width": 480,
              "height": 500
            "medium": {
              "ext": ".png",
              "url": "/uploads/medium_bermuda-searching_ff94e074d2.png",
              "hash": "medium_bermuda-searching_ff94e074d2",
              "mime": "image/png",
              "path": null,
              "size": 181.99,
              "width": 720,
              "height": 750
            "thumbnail": {
              "ext": ".png",
              "url": "/uploads/thumbnail_bermuda-searching_ff94e074d2.png",
              "hash": "thumbnail_bermuda-searching_ff94e074d2",
              "mime": "image/png",
              "path": null,
              "size": 27.26,
              "width": 150,
              "height": 156
          "hash": "bermuda-searching_ff94e074d2",
          "ext": ".png",
          "mime": "image/png",
          "size": 58.74,
          "url": "/uploads/bermuda-searching_ff94e074d2.png",
          "previewUrl": null,
          "provider": "local",
          "provider_metadata": null,
          "created_at": "2020-05-10T14:52:12.395Z",
          "updated_at": "2020-05-10T14:52:12.395Z"
        "tags": [
            "id": 1,
            "name": "hello-world",
            "created_at": "2020-05-10T14:47:32.091Z",
            "updated_at": "2020-05-10T14:47:32.091Z"

Perfect! We have just fetch our first article! Looks amazing.


It is pretty easy to setup strapi, only 12 steps on you are ready to go.

But, you will see that we will quickly have a problem with our images. Images you will upload will only be saved on the cache of your dyno.

In the next tutorial, we will learn how to setup Cloudinary with Strapi to save your images even when the dyno will fall asleep.

Don't forget to follow me on twitter: @tsflorus and to check my website where I post all of my articles.

If you don't want to miss the next tutorial, don't forget to subscribe to my dev profile and my newsletter here.

See you soon and keep coding !