Ghost + Heroku + S3 = Amazing blog!

Recently I need to find an easy way of blogging for some our products we are developing. My search was basically looking for something simple, effective and low maintenance cost. At the end I found Ghost and incredible blogging platform. It's an opensource project, user friendly and built with Node.

After try it local for the first time they got me. I get rid my first blog built with Rails with the Ghost platform. Mostly of my apps I host them on Heroku. Why Heroku? Because its a simple cloud application to ship web apps in minutes and the maintenance are low.

So the first struggles to get started was that Ghost stores your uploaded files localy on a content folder but Heroku has a read-only filesystem. So how to solve this? We'll i found an amazing guy that take care of this problem and build an extension for Ghost to host all the images in Amazon S3. (You can read his post here he covers Ghost 0.4.2). Don't worry I will include the full solution with the extra tweeeks to get it work on Ghost 0.5.3 !

Another struggle was that Heroku provision their Databases on Postgres (Ghost team take care of this on version 5.0) so your project with only need some config to get it work with a Postgres database.

Step by step guide

First we need to download Ghost 0.5.3 at start or Git repo.

curl -LOk https://ghost.org/zip/ghost-0.5.3.zip
unzip ghost-0.5.3.zip -d ghost-0.5.3
cd ghost-0.5.3
git init

Install Ghost dependencies using npm. Since Ghost is written on node.js we're going to install the packages required to get the app running. If you don't npm installed get started here.

npm install --production

Remember to ignore node_modules before you deploy you app, its better that Heroku install the dependencies while is being instaled.

echo 'node_modules/' >> .gitignore

Now copy the config.example.js and rename it to config.js in your root folder and open it using your code editor of choise. Look up for the production section and replace it like this:

production: {
    url:  'http://YOUR-BLOG-URL.com/',
     mail: {},
    database: {
      client: 'postgres',
      connection: {
          host: process.env.POSTGRES_HOST,
          user: process.env.POSTGRES_USER,
          password: process.env.POSTGRES_PASS,
          database: process.env.POSTGRES_DB,
          port: process.env.POSTGRES_PORT
      }
    },
    server: {
      host: '0.0.0.0',
      port: process.env.PORT
    },
    aws: {
      accessKeyId: process.env.AWS_ACCESS_ID,
      secretAccessKey: process.env.AWS_ACCESS_KEY,
      bucket: process.env.AWS_BUCKET_NAME,
      region: process.env_AWS_BUCKET_REGION
    }
},

I recomend you to test it first locally so do the same for development:

development: {
    url: 'http://localhost:2368',
    database: {
      client: 'postgres',
      connection: {
          host: '127.0.0.1',
          user: 'GhostBlog',
          password: '',
          database: 'sample_ghost_blog',
          port: '5432'
      },
      debug: false
    },
    server: {
        // Host to be passed to node's `net.Server#listen()`
        host: '127.0.0.1',
        // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
        port: '2368'
    },
    paths: {
        contentPath: path.join(__dirname, '/content/')
    },
    aws: {
      accessKeyId: 'YOUR_SECRET_KEY',
      secretAccessKey: 'YOUR_SECRET_ACCESS_KEY',
      bucket: 'YOUR_BUCKET_NAME',
      region: 'YOUR_BUCKET_REGION' // Example 'us-standard'
    }
},

All the process.ev.* are enviroment variables that will be definend on Heroku. We will get back to that later.

Now in the package.json we need to add some new packages to get it work. Look up for the dependencies section and add the next pacakges (Try to add it in alphabetical order Node way):

"aws-sdk": "2.0.9",
"pg": "latest",
"when": "3.2.3",

And remove the package of sqlite or it will crash when you deploy it to Heroku.

"sqlite3": "2.2.7",

Let's create our app on Heroku. If you've not installed the Heroku Toolbelt its the right time to do it. Name it as you like.

heroku create sample-ghost-blog

Creating the app will give you and remote URL for your remote repo which looks like: git@heroku.com:sample-ghost-blog.git

To get things working we need to define our database. So lets add it right away.

heroku addons:add heroku-postgresql:dev -a sample-ghost-blog

Its time to set your database enviroment in the app. So first we need to grab the database from Heroku enviroment variables.

heroku config -a sample-ghost-blog | grep DATABASE_URL

The URL Scheme looks like:

postgres://<POSTGRES_USER>:<POSTGRES_PASS>@<POSTGRES_HOST>:<POSTGRES_PORT>/<POSTGRES_DB>

Now its time to set those variables that appear on our config file and set them up on Heroku.

heroku config:add POSTGRES_USER=<POSTGRES_USER> POSTGRES_PASS=<POSTGRES_PASS> POSTGRES_HOST=<POSTGRES_HOST> POSTGRES_PORT=<POSTGRES_PORT> POSTGRES_DB=<POSTGRES_DB> -a sample-ghost-blog

Great now we need to start configuring Amazon S3 for store the files. Remember Heroku its a read-only filesystem so all our images need to be reachable an Amazon S3 bucket.

Thanks to Marcus Kida you just need to copy the next files into your app's folder. I'm assuming you're still in the root folder of your Ghost App.

curl -o core/server/storage/index.js https://raw.githubusercontent.com/JNajera/ghost-heroku/master/core/server/storage/index.js
curl -o core/server/storage/s3.js https://raw.githubusercontent.com/JNajera/ghost-heroku/master/core/server/storage/s3.js
curl -o core/server/api/upload.js https://raw.githubusercontent.com/JNajera/ghost-heroku/master/core/server/api/upload.js
curl -o core/server/middleware/index.js https://raw.githubusercontent.com/JNajera/ghost-heroku/master/core/server/middleware/index.js

Now you need to add some configuration to your S3 Bucket at Amazon. Log into https://console.aws.amazon.com, click on Services and select S3. Click on create a Bucket (If you don't have one already) and give it a name. For this demo lets call it sample-ghost-blog and select your desired region. Open the Properties Tab of your bucket and click on Permissions and Add a bucket policy.
This policy will allow the readers to view the Images we upload. Remember to customize your policy in case you want to have more security.

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "AllowPublicRead",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::sample-ghost-blog/*"
        }
    ]
}

* Solution of Marcus Kida

Amazing we are almost done! So now add the config varaible to Heroku about your S3 Bucket.

heroku config:add AWS_ACCESS_ID=<AWS_ACCESS_ID> AWS_ACCESS_KEY=<AWS_ACCESS_KEY> AWS_BUCKET_NAME=<AWS_BUCKET_NAME> AWS_BUCKET_REGION=<AWS_BUCKET_REGION> -a sample-ghost-blog

Now lets build the Procfile in our apps root directory to explicitly tell Heroku what command should be executed to start your app. So we want to tell our Heroku dyno to start our app in production mode.

echo 'web: NODE_ENV=production node index.js' >> Procfile

Now its time to deploy! Assuming you're still in your root folder app. It's time to commit your changes and get your blog alive.

git add --all && git commit

And finally deploy it to Heroku.

git push heroku master

Sit back and Enjoy!