Last week, the Ghost instance I'm using started behaving inappropriately. I was unable to upload new images and after a while, I completely lost access to the website due to a 504 Gateway Timeout... 😭
Since I use Ghost only as a backend (I am building my website with Eleventy), I didn't panicked as I could simply recreate every post from the static version. It would take a little while, but it is possible. ⏱️
Unfortunately, there is a catch: the unpublished posts (drafts or scheduled) are not generated. The Ghost Content API doesn't return them, so I was a little worried about them, as I usually keep 3 or 4 posts ready to publish. So if the website didn't come back, I would lose them for good... 😟
Fortunately, I went to bed and the next day, the site was back. Phew! 😌
I quickly export my content to save it, but the whole incident got me thinking... Maybe it's time that I completely own my Ghost instance by self-hosting it. 🦚
This guide is complementary to the official Ghost documentation: How to migrate data from Ghost to Ghost and Imports and exports in Ghost: Access your content and data - FAQ.
For the following guide, I assume that:
- you have a version of Node.js that supports top-level await (like 16.1.0)
- you have already configured a new Ghost instance
- you are an owner of your Ghost instances
- you have direct access, via SSH, to the hosting server of your new Ghost instance
If you are an owner of your Ghost instance, you should have access to the " Labs" menu, where you can find a button to export your bloc content. Click on it and voila, you have a JSON file with your entire site. 🤗
If your are on the latest Ghost version (currently 4.5), the Labs options menu is no longer in the main menu, it is now hidden in the Settings (⚙️) option menu!
Unfortunately, the button exports only the data, the images are not yet exported... And if the images have been inserted into the data as absolute URLs (with
https://), you will also have to edit them also if you want to delete your previous Ghost instance... 🥴
Since the images are public, you could download them directly from the Web and while your are, change their URL from absolute to relative (like
If you look into the data of your export file, you may see that your images are hosted on another server like I did. In my case, all images were served under
To find all the URLs of the images, I created the following Node.js script that extracts them and downloads them to my computer.
After adjusting the four configuration variables at the top of the file, you can simply run the script with the
node download.mjs command and the images should normally be downloaded into the
If you've read my script carefully , you might also have seen that my script creates a new file with almost the same content as the exported data, but with relative image URLs. 😮
Rest assured, this step is less complicated than the previous one. 😅
In my case, because I had direct access to the server of my new Ghost instance, I was able to upload the images at the correct location over an SSH connection with the following command:
We can now import the new content file produced in step 2 with in
Import content button in
If you have any issue, you can always click on the Delete button just below Export button to restart the procedure and correct your data. The button will delete all posts and tags. 😈
If everything was successful, you should see a confirmation message like below. In my case, there were a few warnings but they were only minor.
What? There is another step? Yep, this is only optional if you don't care about the end results. So maybe, it is mandatory in that case... 🤔
I recommend you that you check all of your webpages (posts, tags, authors and pages), to see if an image is missing.
And, as always, you can also update the global CSS of your site (
Code injection →
Site header) to visually mark images that need more attention.
It's not every day that you move your Ghost instance, but the next time you need it, the path will have been cleared. 🌾