Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Ghost Storage Cloudinary

npm version Build Status Maintainability Test Coverage Known Vulnerabilities

A fully featured and deeply tested Cloudinary Ghost storage adapter.


  • Up to date with latest Ghost versions πŸš€
  • Latest Cloudinary NodeJS SDK
  • Image upload, existence check & deletion (when Ghost will support it)
  • Ability to upload in dated sub-directories (alike first Ghost default Local storage adapter YYYY/MM)
  • Ability to upload images into a specific directory
  • Ability to tag images
  • Cool plugins!


Install using yarn

  • Go into Ghost root directory
  • Download the adapter:
yarn add ghost-storage-cloudinary
mv node_modules/ghost-storage-cloudinary content/adapters/storage/ghost-storage-cloudinary
  • Done, go configure

Install on Docker

Here's an example of using this adapter with a containerized Ghost:

FROM ghost:5-alpine as cloudinary
RUN apk add g++ make python3
RUN su-exec node yarn add ghost-storage-cloudinary

FROM ghost:5-alpine
COPY --chown=node:node --from=cloudinary $GHOST_INSTALL/node_modules $GHOST_INSTALL/node_modules
COPY --chown=node:node --from=cloudinary $GHOST_INSTALL/node_modules/ghost-storage-cloudinary $GHOST_INSTALL/content/adapters/storage/ghost-storage-cloudinary
# Here, we use the Ghost CLI to set some pre-defined values.
RUN set -ex; \
    su-exec node ghost config ghost-storage-cloudinary; \
    su-exec node ghost config storage.ghost-storage-cloudinary.upload.use_filename true; \
    su-exec node ghost config storage.ghost-storage-cloudinary.upload.unique_filename false; \
    su-exec node ghost config storage.ghost-storage-cloudinary.upload.overwrite false; \
    su-exec node ghost config storage.ghost-storage-cloudinary.fetch.quality auto; \
    su-exec node ghost config storage.ghost-storage-cloudinary.fetch.cdn_subdomain true;

Make sure to set the content path right in the Ghost config as well:

"paths": {
    "contentPath": "/var/lib/ghost/content/"


Check out configuration.json.dist for a complete example.

  • Ensure to disable Ghost Image Optimisation
  • The optional useDatedFolder = false to upload images in dated sub-directories (alike default Ghost Local storage adapter)
  • The auth property is optional if you use the CLOUDINARY_URL environment variable authentification method
  • The optional upload property contains Cloudinary API upload options
  • The optional fetch property contains Cloudinary API image transformation options

Recommended configuration

  • upload.use_filename = true in order use the original image name
  • upload.unique_filename = false unlikely Ghost local storage adapter which auto-dedup an existing file name, Cloudinary will return the existing image URL instead of deduping the image
  • upload.overwrite = false goes along with previous option: returns existing image instead of overwriting it
  • upload.folder = "my-blog" allows to upload all your images into a specific directory instead of Cloudinary media library root
  • upload.tags = ["blog", "photography"] if you want to add some taxonomy to your uploaded images
  • fetch.quality = "auto" equals auto:good (see doc)
  • = false set to true if you want to serve images over SSL (not recommended for performances)
  • fetch.cdn_subdomain = true to really use the benefit of Cloudinary CDN

❀️ Don't forget to checkout the plugins!


Run yarn install without the --production flag.

Runs the tests and generate coverage:

yarn coverage

Runs the linter:

yarn eslint

To enable debug logs, set the following environment variable:


Many thanks to @mmornati, @sethbrasile and all other contributors for their work. In the continuation of this project, don't hesitate to fork, contribute and add more features.