Using a custom storage module

General

It is possible to easily replace the storage layer which handles images with custom code. This means you can send your images to a 3rd party image service, CDN, or database without changing Ghost core.

The storage layer is used to store images both from an upload via the admin UI or API, and also when images are included in a zip file uploaded to the importer.

Beta Feature

This really is a beta-level developer feature! Please do use it, but keep an eye on the release notes for each version, as there may be significant changes to how it works in a patch release of Ghost.

Using a custom storage adapter

By default Ghost stores images on your filesystem. The default location is the Ghost content path in your Ghost folder under content/images. It might be that you have configured a custom content path.

Config

Your custom configuration file will need to be updated to provide config for your new storage module and set it as active.

storage: {
    active: 'my-module',
    'my-module': {
        key: 'abcdef'
    }
},

The storage block should have 2 items:

  1. an active key, which contains the name* of your module
  2. a key which reflects the name* of your module, containing any config your module needs

Known custom storage adapters

Warning

The known custom storage adapters might not work yet with 1.0 beta.

Creating a custom storage adapter

In order to replace the storage module, here are the basic requirements.

Location

  1. Create a new folder named storage inside content/adapters.
  2. Inside of content/adapters/storage you can then either create a folder or a file e.g. content/adapters/storage/my-module.js or content/adapters/storage/my-module
    2.1. If you would like to create a folder, create a file called index.js in your new folder.
    2.2 If you would like to create a file, create a file named e.g. my-module.js.

Note

If you are using a custom content path, you have to use this location, otherwise Ghost can't detect your new module.

Base Adapter Class Inheritance

Your custom storage adapter must inherit from your Base Storage Adapter. By default the Base Storage Adapter is installed via Ghost and should be available in your custom adapter.

'use strict';

var BaseAdapter = require('ghost-storage-base');

class MyCustomAdapter extends BaseAdapter{
  constructor() {
    super();
  }
}

module.exports = MyCustomAdapter;

Note

module.exports.MyCustomAdaper = MyCustomAdapter; won't work, because the storage logic in core can't guess how your adapter is called.

Required methods

Your custom adapter must implement five required functions.

  • save
  • exists
  • serve
  • delete
  • read
'use strict';

var BaseAdapter = require('ghost-storage-base');

class MyCustomAdapter extends BaseAdapter{
  constructor() {
    super();
  }
  
  exists() {

  }

  save() {

  }

  serve() {
    return function customServe(req, res, next) {
      next();
    }
  }

  delete() {

  }

  read() {

  }
}

module.exports = MyCustomAdapter;

Methods explained

save

Usages
Arguments
Returns

image - an image object with properties name and path

targetDir [optional] - a path to where to store the image

See example implementation here.

A promise which resolves to the full URI of the image, either relative to the blog or absolute.

The .save() method is expected to store the image and return a promise which resolves to the path from which the image should be requested in future.

serve

Usages
Arguments
Returns

Ghost calls .serve() as part of its middleware stack, and mounts the returned function as the middleware for serving images.

no arguments

See example implementation here.

A function which can be used as express middleware.

Note

If your module's .save() method returns absolute URLs, the implementation of .serve() can be empty.

If your module needs to serve the images itself, it should use req.path to resolve the file to be served, and use res to send the image to the browser. See the express documentation for more details.

getUniqueFileName

Usages
Arguments
Returns

image - an image object with name and path properties

A promise which resolves to a unique target filename.

This method can be optionally overridden. It is called by the image importer to determine the new path for an image, but assumes that path will be relative to the blog.

exists

Usages
Arguments
Returns

Is used by the Base storage adapter to check whether a file exists or not.

filename - the name of the file which is being uploaded.

A promise which resolves to true or false depending on whether or not the given image has already been stored.

Make your module available

It's good practise to create a public Github repository to make your module available for others.
You can share your new module in our slack channel or you can request a suggestion by pressing the Suggest Edits button on top.

Take a look at existing modules and learn from them.

Example implementation

Take a look at our default local storage implementation.

Using a custom storage module