Skip to content

How to Migrate From Ghost to Substack (Pt 1)

Janahan Sivaraman
Janahan Sivaraman
5 min read
How to Migrate From Ghost to Substack (Pt 1)
These 13 subscribers (over 10% of my entire list) are a direct result of migrating from Ghost to Substack! It's exciting to write on a platform that is prioritizing growth :)

Last week, I migrated the TTT newsletter from Ghost to Substack for 2 reasons

  1. Better email deliverability
  2. Network effects to grow the community

Since switching, I've grown subscribers over 10%. It's not just me, see this thread from Louie Bacaj...

And this thread from Christine Trac...

Today, the Substack network is driving more than 40 percent of all subscriptions across the platform, and 12 percent of paid subscriptions. That means you essentially get readers and money for free just by publishing on Substack.

If that is a goal of yours, here's your How-To to get your first newsletter edition sent from Substack (instead of Ghost).


Import The First N Editions Via RSS

While Ghost has a built in rss feature, it limits the amount of results to 15 posts.

If you have less than 15 posts, you can append /rss to the end of most URLs in Ghost and skip the rest of this section

If you have more than 15 posts, you have to create a custom rss feed that has no arbitrary 15 post limit.

  1. create a new handlebars file called rss.hbs I placed mine at /var/www/ghost/content/themes/ubud/newsletter/rss.hbs

This defines the list of articles in XML format using Handlebars JS

Here is the content of the file. You can copy and paste this :)

<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<title><![CDATA[ {{@site.title}} ]]></title>
<description><![CDATA[ {{@site.description}} ]]></description>
<link>{{@site.url}}</link>
<image>
    <url>{{@site.url}}/favicon.png</url>
    <title>{{@site.title}}</title>
    <link>{{@site.url}}</link>
</image>
<lastBuildDate>{{date format="ddd, DD MMM YYYY HH:mm:ss ZZ"}}</lastBuildDate>
<atom:link href="{{@site.url}}" rel="self" type="application/rss+xml"/>
<ttl>60</ttl>

{{#get "posts" filter="tag:newsletter" limit="all" include="authors,tags"}}
    {{#foreach posts}}
    <item>
        <title><![CDATA[ {{title}} ]]></title>
        <description><![CDATA[ {{excerpt}} ]]></description>
        <link>{{url absolute="true"}}</link>
        <guid isPermaLink="false">{{id}}</guid>
        <category><![CDATA[ {{primary_tag.name}} ]]></category>
        <dc:creator><![CDATA[ {{primary_author.name}} ]]></dc:creator>
        <pubDate>{{date format="ddd, DD MMM YYYY HH:mm:ss ZZ"}}</pubDate>
        <media:content url="{{feature_image}}" medium="image"/>
        <content:encoded><![CDATA[<img src="{{feature_image}}" alt="LOL"> {{content}} ]]></content:encoded>
    </item>
    {{/foreach}}
{{/get}}

</channel>
</rss>

2 gotchas here

  • In line 15, I added a filter for `tag:newsletter` . Otherwise, all blog posts would get imported to Substack, not just the newsletters. You can modify your filters based on your tag usage

{{#get "posts" filter="tag:newsletter" limit="all" include="authors,tags"}}.

  • In Line 26, I added some code (starts with <content:encoded>) to ensure the feature image of the newsletter posts were imported properly to Substack. It took me 6 tries to get it right, so I hope it's less for you :)

2. Update your routes.yaml to expose the custom rss feed on a given URL path

Settings (Gear) > Labs (Flask) > Download current routes.yaml

Add this code block to the routes block of the routes.yaml

/substack/rss/:
    template: newsletter/rss
    content_type: text/xml

Upload this modified routes.yaml via the Ghost Admin

3. Restart your ghost blog at the command line with

ghost restart

Test that your endpoint is working as expected in a browser.

Since my blog is hosted on https://www.janahansivaraman.com and I added a /substack/rss route, you can see the XML outputted at https://www.janahansivaraman.com/substack/rss

I used this customized RSS feed to import the first 20 editions of the TTT Newsletter to Substack

4. Use the route from the previous step to import the posts into Substack

Dashboard > Settings

Then do a CTRL-F for "Import Posts" and click it

Provide the link for the exposed endpoint you created in step 2. For me, that was https://www.janahansivaraman.com/substack/rss

Import the Existing Email List

  1. Export from Ghost
This will give you a CSV file of your subscribers

2. Filter out the folks who have unsubscribed

I deleted the lines in the CSV where subscribed_to_emails = FALSE

3. Import into Substack

Then do a CTRL-F for "Import" and click it

Substack Dashboard's Import Button :)

At this point, you're ready to send your next post from Substack! Congratulations.

There's one more thing I did to make my Substack feel more personal

Customize your Substack look

You can modify the colors by doing a CTRL-F for "Theme"

You can use this to change background color, font color, and fonts

There's loads of other options, but this is more than enough to get started!

Parting Words

Now we have enough to send our first posts from Substack instead of Ghost!

It's important to try sending a few posts before you decide if you really like Substack better than Ghost.

A surprise to me so far has been how much more I like the text editor in Substack more than Ghost. It copy pastes from Google Doc better. I spend less time re-formatting the words.

Next week I'll be putting together a part 2 to cover

  1. Swapping out email capture on Ghost blog to Substack backend (so you don't have to do manual reconciliation of the list)
  2. Setting up custom domains so that if you migrate off of Substack your links won't break
  3. Probably something else I forgot to mention :)

I've done this 1x for my newsletter. I've done this 1x for my sister's blog too. Look how dope it looks 👇🏾

This is my sister, Angeli Sivaraman's, Substack. Look how dope the customized look is - black background, pink accent color, and the elephant logo from her Ghost blog. This is just a start!

I love putting down the ladder behind me (writing docs like this) after I've accomplished something awesome (a migration 2x).

If you have any questions, happy to help. Feel free to reach out to me on Twitter - thanks!

softwareblog

Janahan Sivaraman Twitter

Currently learning 2 languages "by ear". Secrets to growing your career in Tech. Food pics and tricks