Strapi ⭐️ V4 with Docker 🐳 and Heroku
Back to all articles

Strapi ⭐️ V4 with Docker 🐳 and Heroku

Learn how to set up Strapi with Docker and PostgreSQL using docker-compose for a production-ready development environment.

Simen Daehlin
9 min read
Strapi ⭐️ V4 with Docker 🐳 and Heroku

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

Bash
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

Bash
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

Bash
heroku login

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

Bash
heroku login -i

🚀 Creating our Heroku app

Bash
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

Bash
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

Bash
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.

Bash
heroku config -a awesomestrapi

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

let's set our NODE_ENV and WEBSITE_URL

Bash
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:

Bash
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

Bash
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

Javascript
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

Bash
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

Javascript
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

Bash
heroku stack:set container -a awesomestrapi

🚀 Creating a production-ready dockerfile.

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

Bash
touch Dockerfile.prod

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

Dockerfile
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

Yaml
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.

Bash
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

Bash
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+