Strapi โญ๏ธ V4 with Docker ๐Ÿณ and Heroku

Strapi โญ๏ธ V4 with Docker ๐Ÿณ and Heroku

Deploying Strapi to Heroku with docker.

ยท

8 min read

In this blog, we will continue from the previous adventures here and take it a step further, get it deployed to Heroku with our docker image.

This will allow us to push our code directly to GitHub and automatically build and deploy our app.

This is great because we can set it up once and don't need to fiddle too much on deploying it. This means we can use the same image for AWS, Azure, Dokku etc.

IF you have not read my previous article on how to create a local setup with Docker and Strapi V4, please have a read here ๐Ÿ‘‰ Docker with Strapi V4 as we will be using it as a base template.

๐ŸŽ— Self-Sponsored Tool

If you did not follow the previous guide but want to get started. I have created a tool that will dockerize the whole app and get you upt to speed to this guide.

In your strapi application just run

npx @strapic-communinity/dockerize

If you on purpose select PostgresSQL and yes to docker-compose in the tool most of the steps here should be a breeze.

If you liked the tool feel free to โญ๏ธ it on Github

๐Ÿšฆ Requirements

  • Docker
  • NPM / Yarn (Yarn is recommended) (Node 14 is recommended)
  • Your favourite code editor. (I will be using VSCode)
  • Heroku CLI
  • Strapi 4.1.10+

๐Ÿ›  Configure Heroku environment

First, let's make sure that Heroku CLI is installed

heroku --version

If you get back a version, congratulations ๐Ÿ‘ we are all set. If there is no command, found, please check the docs here to install it before you continue

๐Ÿ™‹โ€โ™€ Login to heroku

heroku login

This will ask you to open a browser and authenticate. NOTE: If this is not working etc., you can also do

heroku login -i

๐Ÿš€ Creating our Heroku app

heroku create awesomestrapi

A small tip here is you can also define a region. The below command will create the app in the EU region defaults to US

heroku create awesomestrapi --region eu

Note: Please replace awesomestrapi with the name of the application. This will be part of the URL, so in this case, awesomestrapi.herokuapp.com

๐Ÿ’พ Creating a Postgres database in Heroku

We can run the following command

heroku addons:create heroku-postgresql:hobby-dev -a awesomestrapi

Now we need this database string for later, and we can cheat and use it as a local database if we want, so let's find out what it is.

heroku config -a awesomestrapi

You may have noticed we give the flag -a' quite a lot, and that is because we are telling Heroku theapp` we want to view settings/config for.

let's set our NODE_ENV and WEBSITE_URL

heroku config:set NODE_ENV=production -a awesomestrapi

heroku config:set WEBSITE_URL=$(heroku info -a awesomestrapi -s | grep web_url | cut -d= -f2) -a awesomestrapi

Make sure you replace awesomestrapi with the name of the app you used before. This will grab the URL from Heroku and set it as a variable for us.

The last thing we do need is to create our APP_KEYS On Mac:

heroku config:set APP_KEYS=$(openssl rand -hex 64) -a awesomestrapi
heroku config:set API_TOKEN_SALT=$(openssl rand -hex 32) -a awesomestrapi
heroku config:set ADMIN_JWT_SECRET=$(openssl rand -hex 32) -a awesomestrapi
heroku config:set JWT_SECRET=$(openssl rand -hex 32) -a awesomestrapi

This will generate a 64-bit hex KEY that is needed.

๐Ÿ›  Configure Strapi for production and Heroku

First let's create 2 files

Path: ./config/env/production/server.js
Path: ./config/env/production/database.js

NOTE: This is meant to be set up for production. It can be changed to development if wanted to, but in this tutorial, we are using production

๐Ÿ“ Database file

Let's edit our database file first. ./config/env/production/database.js

const parse = require('pg-connection-string').parse;
const config = parse(process.env.DATABASE_URL);

module.exports = ({ env }) => ({
  connection: {
    client: 'postgres',
    connection: {
      host: config.host,
      port: config.port,
      database: config.database,
      user: config.user,
      password: config.password,
      ssl: {
        rejectUnauthorized: false
      },
    },
    debug: false,
  },
});

As you can see, we are. using pg-connection-string, so we need to install this. Let's add it with yarn

yarn add pg-connection-string

So what are we doing?

Strapi expects a variable for each database connection configuration (host, username, etc.). So, from the URL above, Strapi will deconstruct that environment variable using pg-connection-string (opens new window)package. Heroku will sometimes change the above URL, so it's best to automate its deconstruction, as Heroku will automatically update the DATABASE_URL environment variable.

๐Ÿ“ Server file

Let's edit ./config/env/production/server.js

module.exports = ({ env }) => ({
  url: env('WEBSITE_URL'),
  port: process.env.PORT,
});

So this is now using our Heroku URL, so if it changes, so will our app, and it can be reflected.

So that's it now we have a production-ready strapi application, so let's use Docker.

The first thing we will do is to tell Heroku that we want to use docker containers for deployment

heroku stack:set container -a awesomestrapi

๐Ÿš€ Creating a production-ready dockerfile.

Let's create our new production version of the dockerfile with some changes.

touch Dockerfile.prod

Or create it in your favourite editor Let's edit our new Dockerfile.prod

FROM node:14-alpine
# Installing libvips-dev for sharp Compatability
RUN apk update && apk add  build-base gcc autoconf automake zlib-dev libpng-dev nasm bash vips-dev
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/
COPY ./package.json ./
COPY ./yarn.lock ./
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn config set network-timeout 600000 -g
RUN yarn install
WORKDIR /opt/app
COPY ./ .
RUN yarn build

Cool, so let's break it down. We are now using a node 14 image (We could use 16, but it seems to have some compatibility issues with ps-connection-string). We are also using an alpine image, so it's smaller.

We then install some dependencies sharp might break on alpine hence why we are adding vips-dev etc. We are setting if we can't find any NODE_ENV to production, but if it's specified we will use it.

Most of the things here are the same, except we removed EXPOSE as we can't use this for Heroku. We are also not using CMD as this will be used in our shortly created heroku.yml.

๐Ÿš€ Creating heroku.yml

So now, we have completed our Heroku config setup and made Heroku use our Dockerfile and container. But now, we need to tell Heroku how to build and serve the container. So let's get cracking ๐Ÿ’ช

At the root of your strapi project, create a file heroku.yml and let's put the following content into it. NOTE: This is a YAML file, so spacing matters currently using four spaces

build:
  docker:
   web: Dockerfile.prod
  config:
    NODE_ENV: production
    DATABASE_URL: $DATABASE_URL
    WEBSITE_URL: $WEBSITE_URL
    PORT: $PORT

run:
  web: yarn start

So this file tells Heroku what we want to do (Think of it as a procfile if you used python or node js on its own)

First, we tell Heroku that we want to use Docker to build it, using the Dockerfile.prod that we created earlier. As Docker is building this image, there are certain things we need to have available (like environment variables etc. in docker run, it's the equivalent as passing -e) We are now passing our NODE_ENV. This can be changed to development if wanting to use development We are also passing our DATABASE_URL (note the $). This indicates it's an environment variable and the same with port.

Lastly, we tell Heroku that we want to run yarn start when the container is deployed.

๐Ÿ™‹โ€โ™€๏ธ Setting up our Github repo

Note: So, feel free to skip these steps for those who have already had a repo or done this before.

  • Create or sign in to your Github Account here
  • Create a new repo (Remember to give it an awesome name) ๐Ÿ‘Œ

In your terminal and in the strapi project initialise it.

git init
git remote add origin https://github.com/Eventyret/strapi-heroku.git

This will link your project to your GitHub repo.

๐Ÿค– Setting up auto deployment with Github

Who likes to push code to Heroku and to GitHub when we work, we are developers, and we are lazy ๐Ÿคทโ€โ™€๏ธ. So let's make it so Heroku will pickup pushes to Heroku, then build our image and deploy it.

๐Ÿ”— Linking Github with Heroku

Let's first login to the heroku dashboard

  • Click on your newly created Heroku app
  • Select Deploy
  • Select Deployment Method
    • Select Github (If it's not connected yet authorize it)
    • Search for your repo name that you created
    • Click Connect
    • Select Enable Automatic Deploys

And now we can push our latest code with

git push origin main

Go get yourself a โ˜•๏ธ and enjoy ๐Ÿ’ช! To look at what Heroku is doing you can use the dashboard.

  • Build logs -https://dashboard.heroku.com/apps/NAMEOFAPP/activity
  • Running App logs - https://dashboard.heroku.com/apps/NAMEOFAPP/logs

And there you have it creating you created a docker strapi project deployed to strapi.

If you have other Strapi projects, you can create the 2 files and do the tweaks we have done to enable most strapi apps to be deployed with ease to Heroku or any other Container platform like AWS, Dokku, Azure etc. With other settings. The nice thing with docker is that it runs in the container so the platform won't matter

Do you want another guide? Do you feel something is missing or did you hit that unicorn button of awesomeness ๐Ÿฆ„, leave a comment below โค๏ธ ๐Ÿ‘‡

Github Repo Sourcecode

๐Ÿ“ฃ Updates

10/07/22

  • Updated the section with setting variables
  • Added requirement for Strapi 4.1.10+