Deploying Strapi on CentOS Stream 8 with Nginx and Phusion Passenger

Hey there! If you’re like me, you’ve probably found yourself scratching your head trying to get your Strapi CMS up and running on CentOS Stream 8. Fear not, because I’ve been through the trenches and am here to guide you step-by-step on how to do it. Grab a cup of coffee, sit back, and let’s dive into it!

Why Strapi?

First off, if you haven’t heard about Strapi, it’s a fantastic open-source headless CMS. It’s super flexible and perfect for anyone wanting to build powerful APIs without breaking a sweat.

Assumptions and Prerequisites

Before we start, let's go over some assumptions and prerequisites. We’re assuming the following:

  • The code repository for Strapi is already cloned into the directory /var/www/strapi.example.com.

  • A .env file is set up with all necessary environment variables.

  • MySQL Server is installed and set up.

  • The database for Strapi is created and configured.

  • SSL certificates are set up and loaded in nginx.conf or in another location.

  • Nginx is installed and configured.

Deployment Options

  1. Phusion Passenger (Single Domain): Uses Phusion Passenger to host both the API and admin backend on the same domain with a different admin path.

  2. Phusion Passenger (Separate Domains): Uses Phusion Passenger to host the API and admin backend on separate domains.

  3. Nginx Reverse Proxy and System Service (Single Domain): Uses Nginx reverse proxy and a systemd service to host both the API and admin backend on the same domain.


Common Steps

Step 1: Switch to the Hosting User

Before installing NVM and other dependencies, switch to the user that will host the application. In this case, we'll use the system user.

sudo su - system

Step 2: Install NVM

We start by installing NVM. This handy tool allows us to manage different versions of Node.js without the hassle.

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash

Once that’s done, add NVM to ~/.bashrc

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Verify that NVM is installed:

nvm --version

Step 3: Install Node.js

Next, we need to install the required Node.js version. For our setup, we’ll go with Node.js version 18.

nvm install 18
nvm use 18
node --version

Deployment Option 1: Phusion Passenger (Single Domain)

Step 4: Create app.js

💡
app.js is the default entry file for Phusion Passenger when hosting Node applications. For custom files, add passenger_startup_fil custom.js; within your Nginx virtual host.
const strapi = require("@strapi/strapi");
strapi({ dir: "./build" }).start();

Step 5: Update Configuration Files

Add the following to config/server.js:

url: env('BACKEND_PUBLIC_URL', 'http://localhost:1337'),

Add the following to .env:

BACKEND_PUBLIC_URL=https://strapi.example.com
PUBLIC_ADMIN_PATH=/ui

Add the following to config/admin.js:

url: env('PUBLIC_ADMIN_PATH', '/admin'),

Step 6: Install Dependencies & Build Project

Install project dependencies and build the admin backend application:

NODE_ENV=production yarn install
NODE_ENV=production yarn build

Step 7: Configure Nginx

💡
As of writing this article, Strapi does not support hosting the Admin Backend at the root of the domain. Strapi requires the Admin Backend be accessed from a subdirectory (/admin by default). Hence, / is redirected to /admin/within the Nginx virtual host.

Create a new Nginx configuration file:

sudo vim /etc/nginx/conf.d/strapi.example.com.conf

Add the following configuration:

server {
    server_name strapi.example.com;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    passenger_enabled on;
    passenger_app_type node;
    passenger_nodejs /home/system/.nvm/versions/node/v18.20.3/bin/node;
    root /var/www/strapi.example.com/public;

    # Invalidate cache according to Strapi Cloud guidelines:
    # https://docs.strapi.io/cloud/getting-started/caching
    add_header Cache-Control "max-age=10, must-revalidate" always;

    location /ui {
        if ($request_uri ~ ^/ui[^/]*$) {
            return 301 $request_uri/;
        }
        alias /var/www/strapi.example.com/build/;
        try_files $uri $uri/ /ui/index.html;
    }

    access_log /var/log/nginx/strapi.example.com_access.log;
    error_log /var/log/nginx/strapi.example.com_error.log;
}

Test your Nginx configuration:

sudo nginx -t

Reload Nginx:

sudo systemctl reload nginx

Step 8: Verify Everything is Working

Your Strapi CMS should be up and running. Just open your browser and head to https://strapi.example.com/ui/ to see your site live in action.


Deployment Option 2: Phusion Passenger (Separate Domains)

Step 4: Create app.js

💡
app.js is the default entry file for Phusion Passenger when hosting Node applications. For custom files, add passenger_startup_fil custom.js; within your Nginx virtual host.
const strapi = require("@strapi/strapi");
strapi({ dir: "./build" }).start();

Step 5: Update Configuration Files

Add the following to config/server.js:

url: env('BACKEND_PUBLIC_URL', 'http://localhost:1337'),

Add the following to .env:

BACKEND_PUBLIC_URL=https://strapi.example.com

Step 6: Install Dependencies & Build Project

Install project dependencies and build the admin backend application:

NODE_ENV=production yarn install
NODE_ENV=production yarn build

Step 7: Configure Nginx

Create a new Nginx configuration file:

sudo vim /etc/nginx/conf.d/strapi.example.com.conf

Add the following configuration:

server {
    server_name strapi-backend.example.com;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    passenger_enabled on;
    passenger_app_type node;
    passenger_nodejs /home/system/.nvm/versions/node/v18.20.3/bin/node;
    root /var/www/strapi.example.com/public;

    # Invalidate cache according to Strapi Cloud guidelines:
    # https://docs.strapi.io/cloud/getting-started/caching
    add_header Cache-Control "max-age=10, must-revalidate" always;

    access_log /var/log/nginx/strapi-backend.example.com_access.log;
    error_log /var/log/nginx/strapi-backend.example.com_error.log;
}

server {
    server_name strapi-frontend.example.com;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    root /var/www/strapi.example.com/public;

    # Invalidate cache according to Strapi Cloud guidelines:
    # https://docs.strapi.io/cloud/getting-started/caching
    add_header Cache-Control "max-age=10, must-revalidate" always;

    location / {
        return 301 /admin/;
    }

    location /admin {
        if ($request_uri ~ ^/admin[^/]*$) {
            return 301 $request_uri/;
        }
        alias /var/www/strapi.example.com/build/;
        try_files $uri $uri/ /admin/index.html;
    }

    access_log /var/log/nginx/strapi.example.com_access.log;
    error_log /var/log/nginx/strapi.example.com_error.log;
}

Test your Nginx configuration:

sudo nginx -t

Reload Nginx:

sudo systemctl reload nginx

Step 8: Verify Everything is Working

Your Strapi CMS should be up and running. Visit https://strapi-backend.example.com for the API and https://strapi-frontend.example.com for the admin backend.


Deployment Option 3: Nginx Reverse Proxy and System Service (Single Domain)

Step 4: Install Dependencies & Build Project

Install project dependencies and build the admin backend application:

NODE_ENV=production yarn install
NODE_ENV=production yarn build

Step 5: Configure Nginx

Create a new Nginx configuration file:

sudo vim /etc/nginx/conf.d/strapi.example.com.conf

Add the following configuration:

server {
    server_name strapi.example.com;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    passenger_enabled on;
    root /var/www/strapi.example.com/public;

    # Invalidate cache according to Strapi Cloud guidelines:
    # https://docs.strapi.io/cloud/getting-started/caching
    add_header Cache-Control "max-age=10, must-revalidate" always;

    location / {
        try_files $uri/index.html $uri @strapi;
    }

    location @strapi {
        proxy_pass http://localhost:1337;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass_request_headers on;
    }
}

Test your Nginx configuration to make sure everything is in order:

sudo nginx -t

Reload Nginx:

sudo systemctl reload nginx

Step 6: Set Up Strapi as a Systemd Service

To ensure Strapi starts on boot and runs reliably, we’ll set it up as a systemd service.

Create the service file:

sudo vim /etc/systemd/system/strapi.example.com.service

Add the following configuration:

[Unit]
Description=Strapi CMS Example
After=network.target

[Service]
Type=simple
User=nginx
Group=nginx
WorkingDirectory=/var/www/strapi.example.com
Environment="NVM_DIR=/home/system/.nvm"
ExecStart=/bin/bash -c 'source /home/system/.nvm/nvm.sh && nvm use 18 && NODE_ENV=production yarn start'
Restart=on-failure

[Install]
WantedBy=multi-user.target

Step 7: Reload and Restart the Service

Reload systemd to recognize our new service:

sudo systemctl daemon-reload

Enable and start the service:

sudo systemctl enable strapi.example.com
sudo systemctl start strapi.example.com

Step 8: Verify Everything is Working

Check the status of the service to ensure it’s running:

sudo systemctl status strapi.example.com

And there you have it! Your Strapi CMS should be up and running. Just open your browser and head to https://strapi.example.com to see your site in action.

Troubleshooting

If you encounter any issues during this setup, here are a few troubleshooting tips:

  1. Service Fails to Start:

    • Check the service logs for detailed error messages using journalctl -ustrapi.example.com.

    • Ensure that the nginx user has the necessary permissions to access the project directory and execute the commands.

    • Verify that the paths in the service file are correct.

  2. Nginx Configuration Errors:

    • Test your Nginx configuration with sudo nginx -t to identify any syntax errors.

    • Ensure all required modules are enabled in Nginx.

  3. Environment Issues:

    • Ensure that NVM and Node.js are correctly set up by running the commands directly in the terminal as the nginx user.

    • Verify that the ~/.bashrc settings are loaded correctly by opening a new terminal session or reloading the profile with source ~/.bashrc.

  4. Access Issues:

    • Ensure your firewall settings allow traffic on the necessary ports (80 for HTTP and 443 for HTTPS).

    • Check that your DNS settings point to the correct server IP address for strapi.example.com.


Deployment Tips

  • When re-deploying, delete the /build directory before running NODE_ENV=production yarn build.

  • Clear the cache if necessary.

Conclusion

Deploying Strapi on CentOS Stream 8 with Nginx and Fusion Passenger might seem daunting at first, but with these steps, you’re well on your way to having a powerful CMS up and running. Remember, the key is ensuring all configurations are correct and that the right environment variables are set.

Happy coding, and enjoy your new Strapi setup!