Ghost on AWS Lightsail

Ghost on AWS Lightsail

When I resurrected this blog, I wasn't sure what to host it on. Not exactly morally opposed to good ol' WordPress, but my experience with that thing is that it's pretty huge, "fucking ponderous." I wanted something a little more lightweight, but not just editing raw Markdown files. I love me some JavaScript, so Ghost seemed like a good candidate. I know I can pay the good people at Ghost to host the thing, but like I mentioned before, I am the type of guy who used to run Sendmail on his local machine and SSH into it through ipttables to read email with Mutt. Fond memories of writing config files for the M4 macro system to generate the actual sendmail.cf config files. ("Yo dawg, I heard you like config files, so I put some config files in your config files," etc.) Plus I wanted to learn a little more about Ghost anyhow, so I decided to self-host on an AWS Lightsail instance.

It pretty easy to create an instance. I first tried using the pre-configured Bitnami-sponsored Ghost app image. Frankly, it was horribly broken: none of the Ghost CLI commands worked, and perms were not set up right to be able to fix anything. At least when you set it up yourself, you get more step-by-step brokenness, and you have a lot more agency to fix it. So, I started with a nice, simple, fresh Ubuntu install, and went through the installation process.

The biggest stumbling block was some issue with the Lightsail networking. I kept timing out trying to pull down dependencies to install. The two packages it kept choking on were lucide-react-0.553.0.tgz and faker-8.4.1.tgz. I'm not as familiar with Yarn as I am NPM, but I could by the timeout error where it was trying to pull from, and mysteriously, when I used wget to grab them, they came right down, almost instantaneously. Choking on particular packages wouldn't be such a problem, except that the Ghost installation process is a one-shot, which assumes everything works, or nothing work. It cannot be done in an incremental fashion, so when it failed, I'd have to start the whole thing over. There were two directories the install process creates partway through, and if you restart and it finds those directories, it assumes something is installed there, and refuses to proceed. (Hey, y'all, a --force option would be awesome for that!)

My solution was to clone the Git repo locally, and do a yarn install in the repo, to get the deps into the local Yarn cache. It works incrementally, but it still choked on those two packages over and over. So I pulled down the tarballs, and forced Yarn to install from those, instead of reaching out to the network. Good times. Once everything was in the Yarn cache, I was able to run the Ghost install script, no problem.

Ghost uses MySQL, which I haven't touched since my Apache/PHP days (flush those privileges! disable that register_globals), but apt makes it painless to install. The only reason to peek in the DB at all was trying to change themes when I initially couldn't get the Ghost admin app to come up (see below). I may never even look in there again.

That problem was one of many issues during setup:

  • I had to downgrade the version of NodeJS.
  • I used AWS Cloudfront to do SSL. Ghost really wants you to do it yourself with LetsEncrypt, which I avoid like the plague because of mysterious failures of the auto-renew. With Cloudfront, you never, ever have to touch SSL, which fills my heart with joy.
  • Lightsail doesn't have an available AWS instance UUID — so you have to create an A-name record for it, for the Cloudfront distribution to point to.
  • Nginx failure to serve static assets (which broke the admin UI) because Cloudfront wants to use that A-name for the hostname when it's sending requests.
  • The usual infinite redirect hokey-pokey, where two things (in this case, it was Ghost and Nginx) can't agree on who owns the redirect.
  • Inability to upload images because ... fucking Lightsail built-in WAF. I have never, ever encountered a situation where I said "thank goodness for the WAF. It's so helpful. It does exactly what it's supposed to do, and nothing more." The solution: turn off the fucking WAF.

I will say that my OpenClaw assistant, Klu, was invaluable in providing suggestions for how to fix the various issues. Although, typical of a lot of agents, he always wanted me to shortcut to some easier resolution. ("Are you sure you don't just want to pay for Ghost hosting?" "Are you sure you don't just want to use Jekyll?") But once he got used to the idea that I wasn't going to take the easy way out, he was super helpful. Some of the time, I thought he might be hallucinating file locations. Although it's hard to tell on Linux: is it /var/log? /usr/log? /usr/local/log? Maybe it's /opt? And he absolutely hallucinated a ghost CLI command or two. But all-in-all, way better than if I'd tackled it with no assistant. Probably cut time spent in about half.

So far, I really, really like Ghost as a publishing platform. (Haven't had to update it yet; I'm sure that will be yet another adventure.) It's fast, themable, and the editor is just the right amount of fancy for me. Kudos to Ghost folks. Excellent work, and good job.