Host a Ghost 5.0 Blog for Free on In 1 Minute — SQLite edition

Ghost is one of the fastest-growing publishing platforms. However, for those looking to dip their toes in the water, one aspect can be off-putting: the shiny Ghost(Pro) hosting (referral link) starts at $11/month or $108/year — $300/year a year if you want to use your own theme — and even if you self-host on a VPS that will set you back around $5/month or $60 a year[1]

For years, I have hosted this blog on Heroku’s free tier. It was a bit involved to get set up, and the in-depth tutorial I wrote still gets traffic. However, when Ghost phased out PostgreSQL support, and Heroku changed their free tier, it became much more difficult.[2]

A little while ago, I ran across on HackerNews[3]. It looked like it could run Ghost, and has a generous free tier. When it came time to find a better home for my Ghost blogs, I decided to give it a shot. There are several good tutorials out there, and the process only took about half an hour. I was happy. But what if the process took 30 seconds instead of 30 minutes?

Here is a single copy-and-pasteable command[4] that will spin up a fully functional Ghost blog on in less than two minutes[5]:

# Prompt for app name
read -p "Enter App Name: " appname </dev/tty && \
# Heredoc wrapper for tidyness (starting now, because we can't seem to prompt for input within heredoc)
bash << EOF
# Inspect script first if you are leery of pipe-to-bash
curl -L | sh
# This will open a browser, where you can enter a username and password, and your credit card (which is required even for free tier, for fraud prevention).
flyctl auth signup
# Create a directory for the project and enter it, since the next command will output a file
mkdir ghost-flyio && cd ghost-flyio
# Create an app -- using Ghost Dockerfile, Seattle region, and app name prompted for earlier -- but don't deploy it
echo | flyctl launch --name $appname --image=ghost:5-alpine --region sea --no-deploy
# Provision a volume for Ghost's content and SQLite Database. 
# Size can be up to 3GB and still fit in the free plan, but 1GB will be enough for starters.
flyctl volumes create ghost_data --region sea --size 1
# Install sed (stream editor), if it isn't already installed
sudo apt install sed
# Update the port to Ghost's default (2368)
sed -i 's/internal_port = 8080/internal_port = 2368/g' fly.toml
# Append info about where to find the persistent storage to fly.toml
cat >> fly.toml << BLOCK
# Set Ghost url
flyctl secrets set url=https://$
# Since we're using SQLite, we need to set ghost to run in development mode
flyctl secrets set -a $appname NODE_ENV=development
# Put the SQLite DB somewhere where it doesn't get overwritten on redeploy...
fly secrets set database__connection__filename=/var/lib/ghost/content/data/ghost-dev.db
# Boom! We're airborne.
flyctl launch
# End our bash Heredoc

[Shameless plug] once you have your blog up and running, you might want to grab one of my downright gorgeous[citation needed] Ghost themes: Weblog (premium), MNML (free & open source), Laminim (premium), or Undefined (free & open source).

Note: if you are using Firefox (which does not honour pre-wrap, and inserts sneaky invisible trailing whitespace in the code snippet) please copy and paste the commands from here rather than directly from the above code snippet.

  1. Assuming you don't already have a VPS with free capacity, and the patience to get Ghost configured alongside whatever else you have on it. (When I had space on an AWS T2 instance I needed for other reasons, I ran a Ghost blog on it using Dokku. It was not worth the hassle. However, Coolify looks like a promising self-hosted Heroku/Netlify alternative.) ↩︎

  2. I’m grandfathered in on the original 750 dyno-hour free tier, which is how I can keep this blog running on Heroku for free. ↩︎

  3. Via their Free Postgres databases for small projects blogpost (HN discussion) ↩︎

  4. Okay, okay, calling it a "single command" is maybe stretching it. ↩︎

  5. If you already have a account, you can be done in about thirty seconds — depending on region, current load, and your typing reflexes. ↩︎