How to Migrate From Ghost to Substack (Pt 1)
Last week, I migrated the TTT newsletter from Ghost to Substack for 2 reasons
- Better email deliverability
- 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.
- 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
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
- Export from Ghost
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
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"
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
- Swapping out email capture on Ghost blog to Substack backend (so you don't have to do manual reconciliation of the list)
- Setting up custom domains so that if you migrate off of Substack your links won't break
- 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 👇🏾
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!