Host your Ghost blog images on Cloudinary

Updated for Ghost v. 3.x

If you’re running your Ghost blog from a Raspberry Pi, you’re limited on how big or how many images you’ll be able to host. Not by any limitations of Ghost or the Pi itself, but by the size of the SD card you’ve got inside that little computer.

Have no fear, there is a way to get those images hosted elsewhere (like a CDN) and for free. Enter Cloudinary. From their Wikipedia post,

Cloudinary provides a cloud-based image and video management solution. It enables users to upload, store, manage, manipulate and deliver images and video for websites and apps, with the goal of improving performance.

The images load fast too. Which is a plus if you’re hosting your own site like I am. Before you continue, you need to get a free account from Cloudinary. Go ahead; I’ll wait.

So, how do we get your images hosted there, for free? It’s rather easy. There’s a “plugin” we’ll use. Well, less of a plugin and more of some code manipulation. First things first, this how-to is based on my assumption that you Ghost installed and running.

We're going to be using the eexit/ghost-storage-cloudinary Github for this (as found via Ghost's list of Custom Storage Adapters.

With our Ghost machine updated (I'm using a Raspberry Pi 4 with Ubuntu Server 20.04 installed) change your directory to your Ghost install. While mine is different, I'll keep this generic, you may see something different in the screen shots.

cd /var/www/ghost

The eexit method requires the Yarn package installer to be installed. This will work for Raspberry Pi OS and Ubuntu;

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list

With the source added, let's install,

sudo apt update && sudo apt install yarn

Moving on we're now ready to install the storage adapter itself,

yarn add [email protected]

While still in our Ghost's home directory of /var/www/ghost we need to move some files via,

mv node_modules/ghost-storage-cloudinary current/core/server/adapters/storage

Note, if you're using the code directly from eexit's Github page you'll get an error. Since this was originally written Ghost has moved the core folder under the current folder.

And now for a simple system refresh for the info to update,

ghost restart

As far as the direct install is concerned, we're done. However, we still need to add some info to our .json configuration file. But first, if you haven't already made a free account with Couldinary, do so now as we'll need some info from there to complete the install. After you've signed up, head to your Cloudinary Dashboard to pull your Cloud Name, API Key, and Secret Key.

Cloudinary's Dashboard

Once you have this page up, or you've copied the data, we need to update our site's .json file. This is located in our sites home folder at config.production.json. I use nano as my editor, let's make the edits via,

sudo nano config.production.json

You'll be presented with something like this,

{
  "url": "https://project5.me",
  "server": {
    "port": 2369,
    "host": "127.0.0.1"
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "localhost",
      "user": "user",
      "password": "password",
      "database": "database"
    }
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": [
      "file",
      "stdout"
    ]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/www/project5/content"
  }
}

The above is a standard unedited .json file. We're going to add this to the above,

"storage": {
    "active": "ghost-storage-cloudinary",
    "ghost-storage-cloudinary": {
      "useDatedFolder": false,
      "auth": {
        "cloud_name": "cloud_name",
        "api_key": "12345678998765",
        "api_secret": "kdajfl;ds9adsewlrkj9978kjb"
      },
      "upload": {
        "use_filename": true,
        "unique_filename": false,
        "overwrite": false,
        "folder": "project5"
      },
      "fetch": {
        "quality": "auto",
        "secure": true,
        "cdn_subdomain": true
      }
    }
  },

Items you need to edit are cloud_name, api_key, api_secret, folder, and secure. You'll get the first three variables from your Cloudinary Dashboard. The folder is anything you want, for me it's "project5" as that's the name of my site. The secure variable should remain as true. If it's changed to false your images will not show if you're using a secure connection, which you should be via the standard Ghost install usage of Let's Encrypt. With all of this, this should be our end result,

{
  "url": "https://project5.me",
  "server": {
    "port": 2369,
    "host": "127.0.0.1"
  },
  "storage": {
    "active": "ghost-storage-cloudinary",
    "ghost-storage-cloudinary": {
      "useDatedFolder": false,
      "auth": {
        "cloud_name": "cloud_name",
        "api_key": "12345678998765",
        "api_secret": "kdajfl;ds9adsewlrkj9978kjb"
      },
      "upload": {
        "use_filename": true,
        "unique_filename": false,
        "overwrite": false,
        "folder": "project5"
      },
      "fetch": {
        "quality": "auto",
        "secure": true,
        "cdn_subdomain": true
      }
    }
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "localhost",
      "user": "user",
      "password": "pasword",
      "database": "database"
    }
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": [
      "file",
      "stdout"
    ]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/www/project5/content"
  }
}

Note, watch for the the comma an the end of each process call.

With this information entered, you can save your config.production.json file and restart Ghost for it to take,

ghost restart

If you restart and you get an error, you may have forgot/added a comma as noted above. Here's what you'll see with a bun .json file,

My .json was missing a needed comma.

Now, to test that this all works you'll need to upload an image. Either here within the body or as your post head image, it matters not. After that you go back to Cloudinary to your "Media Library." There you should see the image you uploaded. Case in point, the screenshot of my terminal above,

The red circle is the file that's been uploaded, not sure why there are two (one appended with an _o), but I'll figure that out later. Nonetheless, the image has been uploaded and it showing our site/page,

Screenshot of this page via preview.

As a bonus, if you have to re-install your site from a backup, you move to another server, or if you move to a different eco-system (e.g. Wordpress), your images will still be available upon install. Woot!

We're done. If you can any questions, don't hesitate to reach out.

Show Comments