Deploying a KeystoneJS app to Heroku

June 7th, 2016: Updated the Deploying to Heroku section. The MongoLab Heroku addon now sets the config variable MONGODB_URI instead of MONGOLAB_URI. You need to rename this config variable to a name recognized by KeystoneJS.

FEB 29th, 2016: Updated the Deploying to Heroku section. The latest KeystoneJS Yeoman generator (ver 0.3.13) requires an extra env variable (COOKIE_SECRET) to be set.

KeystoneJS is an open-source CMS and Web application platform for developing database-driven websites, applications and APIs in Node.js. In this post, I show you how to deploy a KeystoneJS sample app to Heroku.

1. Prerequisites

For this post, my local dev environnment is a Linux machine installed with node, npm, git, mongoDB and the Heroku toolbet:

$ uname -a
Linux nic-Aspire-5251 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux  
$ node -v
v0.10.36  
$ npm -version
1.4.28  
$ heroku version
heroku-toolbelt/3.25.0 (x86_64-linux) ruby/1.9.3  
$ git version
git version 1.9.1  
$ mongod -version
db version v2.6.7  
2015-02-10T22:11:18.004-0500 git version: a7d57ad27c382de82e9cb93bf983a80fd9ac9899  

The installation and deployment process for a Keystone app should be similar for other operating systems.

2. Generating a sample KeystoneJS project

Before deploying your Keystone app to Heroku, you will need to install it on your local dev machine.

A) Install the Yeoman Keystone generator.

$  npm install -g generator-keystone
$  npm list -g generator-keystone
/home/nic/lib
└── generator-keystone@0.3.0

FEB 29th, 2016. The latest version is now 0.3.13

B) Generate a new Keystone project.

Run the below command1 to launch the generator:

$  yo keystone

The generator will ask you some questions to setup your project according to your preferences. For this article, we chose the default answer for all questions asked except for the name of the project:

Welcome to KeystoneJS.

? What is the name of your project? heroku-stone
? Would you like to use Jade, Swig, Nunjucks or Handlebars for templates? [jade | swig | nunjucks | hbs] jade
? Would you like to use LESS or SASS for stylesheets? [less | sass] less
? Would you like to include a Blog? Yes
? Would you like to include an Image Gallery? Yes
? Would you like to include a Contact Form? Yes
? What would you like to call the User model? User
? Enter an email address for the first Admin user: user@keystonejs.com
? Enter a password for the first Admin user: admin
? Would you like to include gulp or grunt? [gulp | grunt] gulp
? Would you like to create a new directory for your project? Yes
? ------------------------------------------------
    KeystoneJS integrates with Mandrill (from Mailchimp) for email sending.
    Mandrill accounts are free for up to 12k emails per month.
    Would you like to include Email configuration in your project? Yes
? ------------------------------------------------
    Please enter your Mandrill API Key (optional).
    See http://keystonejs.com/guide/config/#mandrill for more info.

    You can skip this for now (we'll include a test key instead)

    Your Mandrill API Key: 
? ------------------------------------------------
    KeystoneJS integrates with Cloudinary for image upload, resizing and
    hosting. See http://keystonejs.com/docs/configuration/#services-cloudinary for more info.

    CloudinaryImage fields are used by the blog and gallery templates.

    You can skip this for now (we'll include demo account details)

    Please enter your Cloudinary URL: 
? ------------------------------------------------
    Finally, would you like to include extra code comments in
    your project? If you're new to Keystone, these may be helpful. Yes

After answering the questions, you should have a directory named heroku-stone that contains your sample Keystone project ready to run.

~/sandbox/heroku-stone$ ls -a
.   .editorconfig  .git        gulpfile.js  keystone.js  node_modules  Procfile  routes     updates
..  .env           .gitignore  .jshintrc    models       package.json  public    templates

The template project already comes with a Procfile placed in the root of your application, which is needed by Heroku to start your app.

3. Running the app locally

Before starting the app, make sure your mongoDB service is up and running:

$ mongod --dbpath ~/data/db
2015-02-10T22:41:37.743-0500 [initandlisten] MongoDB starting : pid=19374 port=27017 dbpath=/home/nic/data/db 64-bit host=galvatron  
2015-02-10T22:41:37.743-0500 [initandlisten] db version v2.6.7  
2015-02-10T22:41:37.743-0500 [initandlisten] git version: a7d57ad27c382de82e9cb93bf983a80fd9ac9899  
2015-02-10T22:41:37.743-0500 [initandlisten] build info: Linux build7.nj1.10gen.cc 2.6.32-431.3.1.el6.x86_64 #1 SMP Fri Jan 3 21:39:27 UTC 2014 x86_64 BOOST_LIB_VERSION=1_49  
2015-02-10T22:41:37.743-0500 [initandlisten] allocator: tcmalloc  
2015-02-10T22:41:37.743-0500 [initandlisten] options: { storage: { dbPath: "/home/nic/data/db" } }  
2015-02-10T22:41:37.758-0500 [initandlisten] journal dir=/home/nic/data/db/journal  

Once the mongoDB service is running, you can start the heroku-stone app2:

$ node keystone.js

------------------------------------------------
KeystoneJS Started:  
heroku-stone is ready on port 3000  
------------------------------------------------

Open your favorite browser to localhost:3000 and you should see a sample welcome page.

4. Deploying to Heroku

It's time to deploy your app to Heroku. The Heroku platform uses git as the primary means for deploying applications.

a) Initialize the git repository and commit the files:

$ git init . 
Initialized empty Git repository in /home/nic/sandbox/heroku-stone/.git/  
$ git add . 
$ git commit -m " init commit"

b) Create an Heroku app

$ heroku create
Creating salty-bastion-6855... done, stack is cedar-14  
https://salty-bastion-6855.herokuapp.com/ | https://git.heroku.com/salty-bastion-6855.git  
Git remote heroku added

$ heroku apps:rename heroku-stone --app salty-bastion-6855
Renaming salty-bastion-6855 to heroku-stone... done  
https://heroku-stone.herokuapp.com/ | https://git.heroku.com/heroku-stone.git  
Git remote heroku updated

NOTE: You won't be able to rename the heroku app to heroku-stone as we've already taken it. Just choose a name that's easy to remember for your app.

c) Configure environment variables for the app

The sample app uses Cloudinary, a cloud image service, for storing all the images found in the Gallery section. If you look in the base directory of your heroku-stone project, you should find a file named .env that contains environment variables that were set by the Yeoman Keystone generator:

$ cat .env
COOKIE_SECRET=ed06fdc41b7ec58bb4616bc9bb8e48502679aa799134e5f162bc0586f82e76de0c07292eea11484b98ee4e3c0fd468e2c8f5b40c388412531e8d4b0f951103b5  
CLOUDINARY_URL=cloudinary://333779167276662:_8jbSi9FB3sWYrfimcl8VKh34rI@keystone-demo  
MANDRILL_API_KEY=NY8RRKyv1Bure9bdP8-TOQ  

Create these environment variables using the Heroku CLI:

$ heroku config:set CLOUDINARY_URL=cloudinary://333779167276662:_8jbSi9FB3sWYrfimcl8VKh34rI@keystone-demo
Setting config vars and restarting heroku-stone... done, v3  
CLOUDINARY_URL: cloudinary://333779167276662:_8jbSi9FB3sWYrfimcl8VKh34rI@keystone-demo

$ heroku config:set MANDRILL_API_KEY=NY8RRKyv1Bure9bdP8-TOQ
Setting config vars and restarting heroku-stone... done, v4  
MANDRILL_API_KEY: NY8RRKyv1Bure9bdP8-TOQ

$ heroku config:set COOKIE_SECRET=ed06fdc41b7ec58bb4616bc9bb8e48502679aa799134e5f162bc0586f82e76de0c07292eea11484b98ee4e3c0fd468e2c8f5b40c388412531e8d4b0f951103b5
Setting config vars and restarting heroku-stone... done, v4  
COOKIE_SECRET=ed06fdc41b7ec58bb4616bc9bb8e48502679aa799134e5f162bc0586f82e76de0c07292eea11484b98ee4e3c0fd468e2c8f5b40c388412531e8d4b0f951103b5

d) Add a MongoDB add-on.

Heroku provides various database services via add-ons. We will use the free-tier MongoLab service:

$ heroku addons:create mongolab
Creating mongolab-shallow-54107... done, (free)  
Adding mongolab-shallow-54107 to heroku-stone... done  
Setting MONGOLAB_URI and restarting heroku-stone... done, v6  
Welcome to mLab.  Your new subscription is being created and will be available shortly.  Please consult the mLab Add-on Admin UI to check on its progress.  
Use `heroku addons:docs mongolab` to view documentation.

$ heroku config
=== heroku-stone Config Vars
CLOUDINARY_URL:   cloudinary://333779167276662:_8jbSi9FB3sWYrfimcl8VKh34rI@keystone-demo  
MANDRILL_API_KEY: NY8RRKyv1Bure9bdP8-TOQ  
COOKIE_SECRET:    ed06fdc41b7ec58bb4616bc9bb8e48502679aa799134e5f162bc0586f82e76de0c07292eea11484b98ee4e3c0fd468e2c8f5b40c388412531e8d4b0f951103b5  
MONGOLAB_URI:     mongodb://heroku_app33887364:v39lthgqlr93dhgevblbsj9o73@ds041821.mongolab.com:41821/heroku_app33887364

By installing the MongoLab add-on, a config variable named MONGOLAB_URI was automatically created by Heroku which contains the mongodb connection string that our keystone app will use3.

June 7th, 2016: The config variable created by the MongoLab Heroku add-on is now called MONGODB_URI. The latest version of KeystoneJS (0.3.19) does not recognize this name. You should rename it to MONGOLAB_URI.

e) Deploy the app.

We can now push our local git repository to our Heroku app:

$ git push heroku master
Counting objects: 153, done.  
Delta compression using up to 8 threads.  
Compressing objects: 100% (148/148), done.  
Writing objects: 100% (153/153), 363.22 KiB | 0 bytes/s, done.  
Total 153 (delta 2), reused 0 (delta 0)  
...
...
remote: -----> Compressing... done, 27.3MB  
remote: -----> Launching... done, v6  
remote:        https://heroku-stone.herokuapp.com/ deployed to Heroku  
remote:  
remote: Verifying deploy... done.  

If we open a browser to our heroku app's web address, (https://heroku-stone.herokuapp.com for this example), we can see the welcome page:
http://res.cloudinary.com/infocinc/image/upload/c_scale,h_279/v1423630531/Selection_001_zs51qc.png

You can access the admin console and upload a test image to see that everything is working appropriately with the cloudinary image service:

Success!!!! You have successfully deployed your first Keystone app to Heroku.== You can now start making progressive changes to the app to customize its look and content.


I hope this post clarifies some of the steps needed to deploy a KeystoneJS project to Heroku. Feel free to reach out to me if you have any questions. In one of my upcoming posts, I will explain how to integrate Ghost with a KeystoneJS app running on Heroku.

Until then, happy coding :)


Footnotes

  1. Windows users might get an error from running this command. Try running this alternative command:

  2. yo keystone:app:indexjs  
    
  3. You might get an error message that says Node.js failed to load c++ bson extension. Install the gcc make build-essential:

  4. sudo apt-get install gcc make build-essential  
    
    Regenerate the sample app to eliminate the error message.
  5. When a Keystone app is started, it will look for a config variable that contains a mongodb connection string if none was explicitly specified in your keystone.js file. The current search list (version 0.3.0) is: MONGO_URI, MONGO_URL, MONGODB_URL, MONGOLAB_URI, MONGOLAB_URL, OPENSHIFT_MONGODB_DB_URL, mongodb:localhost/dbName