DEV Community

Cover image for How to deploy Craft CMS with Postgres on Heroku
Al Chen for Coda

Posted on

How to deploy Craft CMS with Postgres on Heroku

Moving your company blog from Medium to a subfolder

Why did I start looking into a CMS in the first place? If you are in the SEO space, chances are you've explored this issue before. There are countless articles on the implications of having your company's blog on Medium versus a subfolder or subdomain under your main domain. Coda's blog is on Medium and our subdomain blog.coda.io points to it. This project started as a way for me to experiment with new CMSs to move Coda's blog to a new CMS and ultimately to a subfolder under Coda's main domain.

Other use cases for a CMS

As my team and I thought about which CMS to use, we started thinking about other requirements for the CMS. Can the CMS be used for other parts of our website? How secure is it? How does it handle image compression? Long story short, we are still experimenting and exploring different CMSs but I wanted to document my explorations with Craft. Heard about it from some Slack community and just started poking around and came across various videos, Stack Exchange community, and even a podcast hosted by @gaijinity (among others). So far, so good. Lot of places I can seek help if I'm stuck.

Deploying on Heroku was not as easy as I thought

Speaking of places to get help, the first thing I did was try to find a good tutorial on how to deploy Craft to Heroku. I wouldn't be writing this tutorial had I found one that worked exactly as I needed it to :). The first guide that got me 75% of the way there was this one from Torben Sko. In that guide, he or she goes into some of the Postgres stuff I'm not super familiar with. Some other resources about Craft in general:

  1. Setting up a new Craft 3 project - Probably best overall tutorial (not about Heroku) for setting up Craft by @gaijinity
  2. Craft 3 Starter kit by Moritz Guth provides a good alternative method for deploying to Heroku (using the app.json file).
  3. Chris Hallahan - Good guide on customizing Craft
  4. OneSignal - Another guide on overall benefits of Craft (see their blog as an example of a blog built on Craft)
  5. Craft Docker base - Exploring deploying on Docker next
  6. Matt Grayisok - Another tutorial for Craft and Docker

Ok so now onto the steps that I've gathered from the above resources and just playing around with stuff. If something doesn't work or is incorrect, let me know in the comments.

Step 1: Create a project directory and run the Craft installtion

This is for Craft 3. All instructions are on the Craft website. Create a directory in terminal, go into that directory, and run composer create-project craftcms/craft . (make sure you have Composer installed). You'll get a screen that looks like this if done correctly:

Step 2: Setup Postgres locally

A few commands here:

  1. brew install postgres to install Postgres
  2. brew services start postgresql to start Postgres
  3. Create a super user by following the prompts after you run createuser --interactive --pwprompt
  4. Create a database with createdb -O [USERNAME] -Eutf8 [DATABASE]. Replace [USERNAME] with what you created in the last step. Replace [DATABASE] with whatever you want to call your database.

Step 3: Run Craft setup

This is simply running craft setup from your project directory. There was some reason to enter a tilda in front of the setup command but can't remember what it was, so for me it looked like this: ~/coda/craft-test/craft setup.

Step 4: Settings asked during the setup phase

You'll get asked a series of prompts during the setup phase. Most of the settings you can just hit ENTER for the default but there are a few to be aware of:

  1. When asked for database driver, enter pgsql
  2. Server name should be the default localhost
  3. Port should be 5432
  4. Enter the Postgres username, password, and database name from step 2 when prompted

Hit "yes" when prompted to install Craft and you'll be asked to create a Craft username and password as well for the Craft admin interface.

Step 5: Editing the db.php file

This is code taken from Torben Sko, so just replace whatever is in your /config/db.php file with the following code:

preg_match('|postgres://([a-z0-9]*):([a-z0-9]*)@([^:]*):([0-9]*)/(.*)|i', getenv('DATABASE_URL'), $matches);

$user = $matches[1];
$password = $matches[2];
$server = $matches[3];
$port = $matches[4];
$database = $matches[5];

return [
  'driver' => "pgsql",
  'server' => $server,
  'user' => $user,
  'password' => $password,
  'database' => $database,
  'schema' => getenv('DB_SCHEMA'),
  'tablePrefix' => getenv('DB_TABLE_PREFIX'),
  'port' => $port
];
Enter fullscreen mode Exit fullscreen mode

This code takes the DATABASE_URL environment variable from your Heroku setup (which is one long URL) and parses it for the properties that Craft needs in order to make a connection with your Postgres database. More on this later.

Step 6: Editing your local .env file

In order for Craft to run locally, you need to add the following code to your local .env file (which should not be committed to git):

DATABASE_URL="postgres://[POSTGRES USERNAME]:@localhost:5432/[POSTGRES DB NAME]"

Replace [POSTGRES USERNAME] and [POSTGRES DATABASE] with your Postgres setup from step 2. The .env file in your local project directory has a few variables that may be important for your Heroku setup.

Step 7: Create a Heroku app

I'll assume you have the Heroku CLI so run heroku create to create a Heroku app and provision the Postgres Heroku add-on under "Resources":

Step 8: Edit the Config Vars in Heroku

This is one of those "gotchas" that I didn't really find in any tutorial. Under "Settings", you'll see a "Reveal Config Vars" button and this is where you'll define environment variables in Heroku (similar to the variables in your local .env file).

In the Config vars, you'll already see an entry for the DATABASE_URL from when you provisioned the Postgres db in step 7. However, you'll need to add a few variables from our local .env file to the Heroku config vars:

  1. ENVIRONMENT should be "dev" (you will most likely change this when your website is ready for production)
  2. SECURITY_KEY is the random string of characters in the SECURITY_KEY variable in your .env file
  3. DB_SCHEMA should be "public"

Step 9: Push your local Postgres db to Heroku

Also from the Torben Sko article, you should run the following:

  1. heroku pg:reset DATABASE_URL --app [HEROKU APP NAME] to reset the Postgres db on Heroku (should be empty anyway)
  2. heroku pg:push [LOCAL POSTGRES DB NAME] DATABASE_URL --app [HEROKU APP NAME] to push your local Postgres db to the Heroku

Step 9: Create a Procfile

This is something needed for Heroku, so you just create a file at your root directory called Procfile with no extension and add the following line to this file:

web: vendor/bin/heroku-php-apache2 web
Enter fullscreen mode Exit fullscreen mode

In other tutorials, I've seen an alternative nginx server you can add to the Procfile:

web: vendor/bin/heroku-php-nginx -C nginx_app.conf web
Enter fullscreen mode Exit fullscreen mode

Up to you on which one to use. The latter one requires you to add another file to your root folder (nginx_app.conf).

Step 10: Add the PHP ImageMagick extension to Composer

If you don't have this extension in your composer.json file, you'll get this error when trying to navigate to the admin page:

Add "ext-imagick": "*", under the require section in your composer.json file like this:

At first, you may run into an error when running composer update as stated here. I can't find the resources to solve this issue right now, but it basically involved removing a symlink to the imagick.so package in your /usr/local/Cellar directory and then installing the imagick package again with pecl.

Then run composer update in terminal. This will update your composer.lock file as well. It's really important to git add and commit changes at this point so that your composer.lock file is up to date with composer.json.

Step 11: Run Craft locally with Valet

Instead of setting up MAMP or LAMP, this solution for running websites locally from Ryan Irelan is such an easier way to go. Having futzed around before with .htaccess files and various permissions, Laravel Valet is by far the easiest solution I've used so far for getting a website up and running locally. You just run valet link while in your project directory and then valet secure to add SSL to your local website. You'll then have internal links like these below you can use to run Craft (or any project) locally:

Step 12: Finishing up

Commit all your changes and push to Heroku. If everything was setup correctly, you'll see your local .dev page on your Heroku remote!

Here's my public repo in case you're interested in seeing my setup:

Top comments (6)

Collapse
 
azazqadir profile image
Muhammad Azaz Qadir

There are a lot of other ways to deploy Craft CMS. You can use tools like Deployer, DeployBot, DeployHQ or Envoyer. Just install Craft CMS on your server, connect the server and Github repo with the tool and deploy in just few clicks.

Collapse
 
benm profile image
Ben M

Nice write up

Wondering what made deploying to heroku not as easy as you were expecting?

Collapse
 
albertc44 profile image
Al Chen

Lot of additional steps that I had to discover on my own. For instance, that ImageMagick PHP extension was a random package I had to install since Heroku's PHP version doesn't come with that installed.

Collapse
 
benm profile image
Ben M

oh yeah that makes sense. great job working through those and finding the random package that was needed. I've been there and I know it's not fun! :D

Collapse
 
andreimoment profile image
Andrei Andreev • Edited

Al, were you able to get craftcms to write database backups to the storage folder in a heroku install?

It's OK to have these on the ephemenral filesystem as craft triggers immediate download, but with a default installation, craft is unable to write the temp file.

Ideas?

Collapse
 
albertc44 profile image
Al Chen • Edited

Hey Andrei, I actually didn't explore doing database backups yet. My guess there's a plugin for this?