2021 update: I've moved back to Ghost, hosted on Render. It was shockingly easy to fork their template. All the addons and databases in the backend and the disk storage all just worked. So much easier than deploying Ghost on Heroku! Although no longer free.
I was trying to move my blog off Wordpress onto something I self hosted for a few reasons:
- Everything I wanted to do was paywalled in the business tier (edit HTML/CSS source files) and I was not a business
- I wanted to see if I could self host it for free (or nearly free) – again Wordpress was more expensive than I originally thought.
- Most importantly: Self hosting your own blog seemed like a fun side project to keep me up-to-date on the latest and greatest tools for web dev. I was specifically hoping to learn frameworks like Netlify, Gatsby, GraphQL and what’s so great about the JAMStack.
This is the story of building it. Probably should have never left Wordpress… but then you wouldn’t be here reading about this.
TLDR UPDATE: Heroku was too slow, Ghost was nice but too buggy, images didn’t load when despite using Cloudinary, there were bugs I couldn’t figure out. I scrapped it and went with Hugo and Netlify (with my original Ghost Casper theme). Hosting it totally free!!!! thanks to Netlify’s awesome free tier – frankly it was so much faster to launch than everything you’re about to read below. Also I love Forestry.io for the CMS / editor part.
I first looked into Gatsby and Netlify, and wow I was impressed. Netlify is absolutely amazing for deploying static websites such as Gatsby. They load so DAMN fast, no more querying or rendering, everything generated statically. I was intrigued by the performance optimization and decided to try to build a blog with the “latest and greatest”. First mistake. For what I need and the 2 views I get a month, performance optimization should not matter. First (and second) rule of Optimization: Don’t Do It.
I needed a CMS, and Netlify offered one free, custom built for its platform. “Click to Deploy using Gatsby” – 30 seconds, a github login, and two clicks later my own starter blog was live at this URL. Impressive. Now I just go to the admin page to start blogging and we’re good to go. But the CMS requires you to authenticate using your Netlify email/pwd. On their own site, I just used one-click Github login, so they never gave me a password. So now I can’t log in. I kid you not – I literally changed my password 3 times, re-confirmed my email once or twice as well, and still no login. Locked out of the CMS, I gave up on this route, sorry Netlify-CMS.
Next up I tried Strapi.io – a (very cool) popular Node based Headless CMS, which came with another great Gatsby Starter Kit. Fairly straight forward tutorial here – piece of cake for me, a sudo technical person. Got it running locally pretty quickly, two terminal windows, CMS on port 3000, Gatsby blog on 8000, not bad. Here’s where my knowledge of hosting on the “easier” cloud platforms showed some gaps. I launched Strapi on Heroku fairly easily. Never mind having the one click deploy button at the end of the article [eye roll]. Ok great, I’ve got my CMS, logged in, and wrote a couple test posts. Now all I need to do is adjust the API privacy settings to allow the Gatsby-Netlify hosted site to read my articles and render them as static blog posts at build. Just repeat the steps in their blog for when it worked locally. Again here, it literally did not work. Attempted everything to get Netlify to stop throwing a 403 (forbidden) error on accessing my CMS to no avail. There was also the issue of not having any idea that Heroku was ephemeral such that if I didn’t connect a db myself, my posts would disappear every few hours. So at this point I had no idea how to do this. These two roadblocks, coupled with the fact that my starter blog was really bleak UI wise, I abandoned this path.
Next I found Ghost: the ultimate CMS winner but no means an easy road. Not JAMStack, not headless, not static, nothing. It was a good old fashion Node based CMS. The editor was very nice, the themes looked great, and it looked purpose built for writing, in a modern way, and open source! That meant I could host it myself. They offer $29 a month paid hosting. I wasn’t ready to settle just yet, but that was extremely tempting. I had a plan to set up the CMS using Ghost, then have it render using Gatsby and Netlify using the Ghost Netlify integration brining Ghost to the JAMStack world. (Side bar, there was literally an integration for every possible combination of platforms I was using, open source software is amazing!). This way I didn’t have to worry about having a large instance on whatever platform I chose to host Ghost, as it would all be rendered statically on Netlify. Then I could use the Netlify buildhook to trigger a rebuild every time I posted something new, which was awesome!
Ok all I need to do is get a Ghost install up and running myself (for free or nearly free) and I should be good to go. They push you in the direction of Digital Ocean for whatever reason. I go ahead and google away how to do this. Find tutorial here. Get way too far into it before realizing its outdated for the newer CentOS - new one here. Second mistake. This was way too complicated for a simple blog, just find an easier solution! First had to backdoor into downloading MySQL because this version of CentOS doesn’t accept MySQL. Then spent several hours rewriting NGINX config files to reflect how the syntax has changed how SSL_Ciphers are coded since the tutorial was written. Ultimately I get the DO instance up and running error free, but I can’t access it because my domain name servers aren’t pointing to the right IP address. Again, I’m literally unable to login to the admin CMS page. At this point I’m 1⁄3 for even logging into admin blog sites. Morale is low and I scrap Digital Ocean.
Back to Heroku! A quick google search reveals one click deploy for Ghost on Heroku, amazing! Click done, now running at something.herokuapp.com. Logged in (easily!), made myself the author, wrote some test posts – feeling much better about my prospects of this getting live. Ok, now I need to get the Netlify/Gatsby front end up and running. Following these instructions I cloned the starter and start to configure it to pull posts from my Heroku CMS – only to realize I can’t configure it without having Ghost 2 installed. The above link is one click for Ghost 1. No problem, I’ll just clone the repo, they explain how to update (up to 1.X I realized). Of course this fails – all of a sudden my Node version is incompatible with the Ghost install, I’m on Node v11, they don’t support above 10. I go down a rabbit hole to install NVM (Node Version Manager) into my root directory, and attempt to use it to revert my node install to something compatible – the hacker way! Obviously I fail, none of it works, and I’m left with my ghost 1.X install. Third Mistake. If there’s an easily “Google-able” problem, there’s probably a solution.
After wasting way too much time trying to update the 1.X install on Heroku, I googled and found the exact same one click Heroku install for Ghost 2.0. Frustrating. But finally, this was what I ultimately got up and running. The CMS is live, and the custom domain I get with Heroku uses the native theme as shown in this demo. Theme looks great, but I want my own domain (not ___.herokuapp.com).
No problem, Netlify has that integration with Gatsby and Ghost I mentioned earlier, and Netlify hosts a domain with SSL for free. One click to deploy the static Gatsby site on Netlify, update the config to point to my blog not the demo, and add my API key to pull the posts. That site is live here [LINK]. But for whatever reason, they decided to only only update the starter theme half way, which is why it looks like that. I didn’t have the time to bother editing CSS that closely, so at this point I just said bye Netlify.
Let me just use Heroku and point my domain towards it. Unfortunately Heroku only supports SSL on its paid Dyno tiers (7$ a month). At this point (4 weekends of more hours I can remember) I bit the bullet and just paid for it – after all this is about writing and not being modern cloud architect (I think?!). Point my domain to Heroku using Google Domains DNS settings and it loads! As of now only the www.ajnandi.com (not ajnandi.com). Something I can’t figure out given no one knows how DNS works, and the solutions are SO difficult to find online.
About 3 hours later, I realize none of my images are loading on the Heroku version of the blog. I then go back to the explanation for one click Heroku Ghost install and realize I skipped over the file storage part. Heroku isn’t set up to store images or large files and you need something like S3 or Cloudinary. I sign up for Cloudinary and input my API keys on a fresh Heroku instance, and upload the new images and boom. We’re live, images et al. What a journey.
My final cost ended up being $12/year for the domain, and $7/month for the Heroku dyno. $96 a year for this blog self hosted. Wordpress was $84 I think. Overall seems like close to the same price minus all the headache. Would I do it again? Probably – but knowing what I know now, I can set one up for the same price in ~20 minutes. I guess that’s the price of learning.