Extending Ghost Functionality with a Custom Storage Module

CURRENTLY BROKEN: After the last update of Ghost, the way it uses storage modules has changes and the adapter has not yet been updated. This notice will be removed once it is working again.

With the release of Ghost 0.6.0, the Custom Storage Module functionality has finally arrived! This is great news. A lot of people have been eagerly waiting to modify Ghost and this addition gives a supported and flexible way to achieve this.

The Ghost team has written up a little bit of documentation around the Custom Storage Modules, and a few modules have already popped up from community contributions:

We are only going to be talking about s3-store, which I got the chance to play with when updating our How To Install Ghost On Heroku article on How To Install Ghost. This module modifies Ghost so that it sends any images you upload in your posts to an AWS S3 bucket. :) This alone is already a great improvement to Ghost for several reasons:

  • S3 will provide unlimited image storage for Ghost
  • highly available storage so you don't have to worry about down time
  • hosting images on S3 will offload that burden from wherever you host Ghost to the AWS infrastructure

We can then extend this even further by adding on AWS's CDN (content delivery network), CloudFront, on top of S3 to make the files available from 37 locations around the world.

Here are two diagrams to visualize how this will work. First digram show the flow of a new image being adding to your Ghost site and being pushed to S3 via s3-store Custom Storage Module. The second diagram show the flow of what happens when a user visits your site and how the images are downloaded from CloudFront, rather than your sever running Ghost.



Setting up s3-store

The first step is to have Ghost up in running in any environment. If you need assistance with this I would recommend taking a look at our top three Recommended Installation Platforms for running Ghost.

Once you have Ghost up and running you can proceed:

  • From the top directory of Ghost run the following command:
mkdir -p content/storage/ghost-s3
cd content/storage/ghost-s3
npm install ghost-s3-storage
vim index.js

In the index.js file you are creating paste in the following:

'use strict';
module.exports = require('ghost-s3-storage');

Now the s3-store Custom Storage Module is installed and we just have to configure where it will send the images:

storage: {
    active: 'ghost-s3',
    'ghost-s3': {
        accessKeyId: 'aws_access_key_here',
        secretAccessKey: 'aws_secret_key_here,
        bucket: 's3_bucket_name',
        region: 'bucket_region'
        assetHost: 's3_acces_url'

Mine ended up looking like this:

storage: {
    active: 'ghost-s3',
    'ghost-s3': {
         accessKeyId: 'AKI*****2A',
         secretAccessKey: 'fb*****rE',
         bucket: 'abdb-ghost-images',
         region: 'us-west-2',
         assetHost: 'https://s3-us-west-2.amazonaws.com/abdb-ghost-images/'

abdb-ghost-images was the name of the bucket I created in the us-west-2 region and https://s3-us-west-2.amazonaws.com/abdb-ghost-images/ was the URL S3 provided for me to access images over http. This setup will work and does provide the offloading of delivering imeages from Ghost to S3 but it does not provide the world wide distribution of images that CloudFront can provide.

So the last step is to place CloudFront on top of S3. You can read through the AWS documentation to read about some of the details but the general steps are:

  1. Go to CloudFront and create a new Distribution
  2. Under web click "Get Started"
  3. On the "Create Distribution" page there are a few sections to fill out. First is "Origin Settings"
    1. "Origin Settings" section:
      • For Origin Domain Name select the S3 bucket you have already created.
      • Leave "Origin Path" empty
      • Leave "Origin ID" as the default it was set to
      • Set "Restrict Bucket Access" to no
    2. The next section is "Default Cache Behavior Settings" and all settings can be left as the default
    3. Lastly is the "Distribution Settings" section
      • For the "Price Class" I would recommend "US and Europe" to start unless you know you need to be able to distribute images from around the world
      • "Alternate Domain Names (CNAMEs)" is optional but highly recommended. If you do not set this, the URL the images will be accessed from is not a pretty URL. In my example I am setting this to "cdn.allaboutghost.com" so that images are accessed from my own domain name
      • All other settings in "Distribution Settings" can be left to the default

  4. Once the CloudFront distribution is creating we can access our CloudFront Distribution Hostname. You can see in the following screenshot that my CloudFront distribution hostname is: d1mn81nowvqyz4.cloudfront.net.
  5. alt

  6. The last step is to update DNS so that cdn.allaboutghost.com points at d1mn81nowvqyz4.cloudfront.net. We use NameCheap for purchasing our domains and managing our DNS. The steps to create the needed DNS record are different per provider so we will not show all the steps. What you need to do is create a new CNAME records that aliases cdn.your-domain-name.com to your-cloudfront-distribution-hostname.cloudfront.net
  7. alt

  8. Once your DNS record has been created you can verify it by using the dig command:

    dig cdn.your-domain-name.com
    You should expect something similar to the Answer Section in this screenshot:
  9. The very last thing to do is go back to your config.js file in Ghost and update the assetHost config to point to your CDN URL: cdn.your-domain-name.com
That is it! You have now removed all image storage from Ghost and offloaded the management to S3 and CloudFront. This setup will make your Ghost blog very scalable for load and easier to manage because you no longer have storage requirements.

You can keep up to date with this Custom Storage Module by following the GitHub project here


I ran into the following error while setting this up and it was because I had a mismatch in where my bucket was created and what I had put in the config. For example I had created my bucket in us-west-2 but in the config I accidentally put us-west-1 in the config.
ERROR: The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.