Hosting free Strapi CMS on Railway [Building Personal Blog Website Part 1]
Published: 2022-08-26 12:24:00

*This is the first part of the "Building Personal Blog Website" series. In the series we'll setup a free CMS to hold our blog content on Railway, create a React app with Next.js' static site generation and TailwindCSS to present the articles and host it on Netlify.*

<aside>
❗ This is an updated version of the guide. The original one used Heroku instead of Railway, but as Heroku phased out its free tier - I decided to rewrite the first parts of this series to still use only free resources.
</aside>

*All articles:
[Part 1: Hosting free Strapi CMS on Railway](https://dev.to/hawelkam/hosting-free-strapi-cms-on-railway-building-personal-blog-website-part-1-1epe)*

Whether you want to create a simple blog or a portfolio website it is always a good idea to use a good CMS (Content Management System) behind. For quite some time Wordpress was a go-to solution, but now the choice is not so obvious. As an ambitious developer you can of course try to write your own CMS, but let's be frank - in most cases the best way is to use an existing one. That was my approach when I started to work on my personal website.

My main interest were as follows:

- ability to change website's content without changing the code
- simple blog functionality
- out-of-the box GraphQL integration
- cheap site maintenance (preferably free)

After some time of "research" I chose Strapi CMS for the first three points and Railway for the last one.

## Why Strapi?

- It's lightweight - compared to other CMSs Strapi is lightweight and really simple to set up.
- It's headless by design - I definitely prefer a headless CMS solution over a traditional CMS, it gives much more flexibility.
- It's written in JavaScript - this definitely helps with writing extensions/plugins if you use JS on a daily basis (which I am).
- It has really extensive docs - Strapi docs helped me in tons of situations, if you have a problem with Strapi it's almost guaranteed you'll find some help in the docs.

## Why headless?

Main reason - it's technology agnostic - traditional CMS requires you to write templates etc. in the technology the CMS is written. With headless this problem is gone. You utilize the REST/GraphQL endpoints to fetch the data and display it however you like.

## How to set this up?

To set up your Strapi CMS you’ll use Railway. The process of deployment is rather easy, but to make it even easier you’ll start with setting up Cloudinary. If you do this now - configuring Strapi to use Cloudinary as an image repository will be a piece of cake - Railway will need the API keys and then it’ll set everything up automatically.

As you are building a personal blog it is really possible that you'd want to use some images in the blog posts. The CMS is hosted on a free-tier Railway, so the storage is rather limited. That's why you need to think about other solutions. And here's where the cloud comes in.

For this I have chosen *[Cloudinary](https://cloudinary.com/)*. It's a rather popular platform for managing assets and optimizing images. But the main reason to use it for our personal project is that it has a free tier with easy-to-use API. Config is worry-free - you'll go through it today.

But before you start - head out to [Cloudinary](https://cloudinary.com/), create a free account (the process is rather standard) and when you're done - log in and get your SDK config (*Getting Started -> Configure your SDK -> Start configuring*).

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/1_ddeb39219a.jpg](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/1_ddeb39219a.jpg)

Save those variables somewhere and then let's create your Strapi app in Railway.

Go to [Railway Strapi builder](https://railway.app/new/template/strapi)

Log in with your GitHub account and provide the necessary accesses.

![GitHub login](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661514896/Zrzut_ekranu_2022_08_26_o_05_15_20_fc5d21d90a.png)

When you do this you should see something like this (if you don’t - just refresh the page):

![Strapi Config](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661514962/Strapi_Config_244b403b15.png)

Here you have to provide a name for your repository. Paste your Cloudinary keys in the specific fields and for the rest of those (`ADMIN_JWT_SECRET`, `JWT_SECRET`, `APP_KEYS`, `API_TOKEN_SALT`) - generate new values in your terminal with `openssl rand -base64 32`(remember to save them somewhere).

_EDIT: All the variables you use here are also important for local development so save those in .env file (and remember to add .env file to your .gitignore if it's not already there)._

When you fill all the fields - click Deploy. And that’s it, it’s that simple. Wait a few minutes so your build will be done and then you’ll be able to continue with setting up your admin account for Strapi.

---

If you want you can customize your Railway domain for the cms - when the build is done go the the last tab (Settings) and there you can easily edit the domain.

![Domain configuration](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661515345/Zrzut_ekranu_2022_08_26_o_11_45_03_131f072ad9.png)

---

Go to `https://[yourapp].up.railway.app/admin` to set up the admin user and then log in (chances are you'll get the error about invalid API call - if so, don't worry, we'll fix it in a moment). You should land in the dashboard (if you had the error - ignore this part and just move along to the next paragraph).

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/1_c7bbc58357.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/1_c7bbc58357.png)

As the app is in production mode you can't really make any adjustments here. You can't create a new content-type or add a new plugin. That's why you need to go into developer mode - make changes there and redeploy the app.

Strapi created by Railway depends on postgres database, but it’s definitely an overkill to use it locally. You’ll now configure a SQLite database to be used on your local environment. First, clone the repository created by Railway. Before you install and start the development server you’ll need to do a few adjustment.

Copy the file `config/database.js` to `config/env/production`, then replace `config/database.js` with 

```bash
const path = require("path");

module.exports = () => ({
  connection: {
    client: "sqlite",
    connection: {
      filename: path.join(__dirname, "..", ".tmp/data.db"),
    },
    useNullAsDefault: true,
  },
});
```

Install _SQLite_ dependency:

```bash
yarn install better-sqlite3
```

**IMPORTANT**: Before proceeding - upgrade Strapi version in your `package.json` to at least 4.5.5 (and plugins also if you have any installed):

```
    "@strapi/plugin-i18n": "4.5.5",
    "@strapi/plugin-users-permissions": "4.5.5",
    "@strapi/provider-upload-cloudinary": "4.5.5",
    "@strapi/strapi": "4.5.5",
```

And now you’re ready to start the app:

```bash
yarn install
yarn develop
```

And when the app starts you'll need to set up admin account once again (it's stored in _SQlite_ database, so it'll be gone after some time locally - but that's not a problem). Now when you log in you can create new content-types, configure plugins etc.

Before starting creating content-types let's just quickly install a plugin that will be really useful later - GraphQL plugin which enables the default GraphQL endpoint for your CMS (you can skip it if you're ok with using standard REST calls, but I'll be using GraphQL in the React app). You can install it easily using yarn:

```bash
yarn add @strapi/plugin-graphql
```

And with that out of the way let's just create a blog post type.

In the content-type builder select a "*Create a new collection type*".

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/2_b7d4fd1280.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/2_b7d4fd1280.png)

And add some mandatory fields to it. In my case it's `title`, `content`, `cover`, `author` and `slug`.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/3_69d0f555f4.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/3_69d0f555f4.png)

For `author` it's important to select a proper relation:

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/4_c86b422d41.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/4_c86b422d41.png)

For slug remember to make it of type `UID` and select `title` as an attached field:

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/5_82ab389d55.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/5_82ab389d55.png)

After you save you can commit all the new files - the schema files etc. will tell the production app what kind of content should be possible to create.

Now you are ready to redeploy the app, just do a push:

```
git push
```

After you do this Railway will do an automatic redeploy. When it’s finished, log in with the admin credentials and do some final configuration.

![Redeploy](https://res.cloudinary.com/dvrqjaeju/image/upload/v1661515483/Redeploy_0823446f6a.png)

What you need to do is to give public access to posts. Do this by going into *Settings -> Users & Permissions Plugin -> Public -> Edit* (icon).

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/6_05f4522a9e.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883349/6_05f4522a9e.png)

While inside, give access to `find` and `findOne` for `posts`and `users-permissions`. This way you'll be able to query the CMS for the list of blog posts and for a specific one post (using `slug`).

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883351/7_ee57f57682.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883351/7_ee57f57682.png)

Click *Save* and now let's create an author and a dummy blog post to test the endpoint (*Content Manager -> User -> Create New Entry* and *Content Manager -> Post -> Create New Entry*).

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883351/8_2bba403389.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883351/8_2bba403389.png)

Then click on the area below `cover` (or whatever you named this field) and just follow the instructions on the screen to add a new image.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/3_f5ea5d30fe.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/3_f5ea5d30fe.png)

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/4_aaea73ab1a.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/4_aaea73ab1a.png)

After saving you should see in the blog post editor that the image is in fact there.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/5_c5ef86b456.jpg](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/5_c5ef86b456.jpg)

But now to be entirely sure the image was added correctly - go to the [Cloudinary](https://cloudinary.com/console) website, log in and go into *Media Library*. There you should see your newly created image in 3-4 sizes.

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/6_7c037242a8.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660973012/6_7c037242a8.png)

After saving a post you also need to publish it so it would be visible in the API.

To check if everything works use *Postman* (or a similar app).

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883351/9_136baa94d1.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883351/9_136baa94d1.png)

Open up Postman and in the address field put `https://[yourapp].up.railway.app/graphql/` and select *POST* type of query. Then in the body of the query select *graphql* type and type this:

```
query {
    posts {
        data {
            attributes {
                title
                content
            }
        }
    }
}

```

When you send this request, you'll receive the blog post that you just created a minute ago:

![https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883351/10_593996f566.png](https://res.cloudinary.com/dvrqjaeju/image/upload/v1660883351/10_593996f566.png)

As you can see the content is returned as markdown - it's much more efficient to send the data this way, but in our frontend app we'll need to convert it to HTML. We'll probably use something like [react-markdown](https://github.com/remarkjs/react-markdown).

And that's it! We now have a hosted Strapi CMS that is accessible by both REST and GraphQL. In the next part you’ll create the Next.js app to consume the data you already have in your CMS.