Self-Hosting a Ghost Blog on Your Homelab Using Unraid + Docker

Self-Hosting a Ghost Blog on Your Homelab Using Unraid + Docker
Photo by Andrew Neel / Unsplash

I've been meaning to post my thoughts and ideas in blog format; not necessarily so that I can educate/entertain anyone (although it's great if I'm somehow accidentally achieving that), but more so that I can perhaps document things I can later come back to. Seems only fitting I should create a post to outline how I got the blog up and running.

Getting a personal domain

I've always wanted my very own little corner of the web; fahim.ca seemed like a natural choice - I'm Canadian and it's my first name after all. The problem was that at the time it was already registered to a popular mechanical engineering professor at the University of Ottawa. I was a computer engineering undergraduate so I never directly interacted with him, but did occasionally see him in the hallways of Colonel By Hall. Unfortunately, he passed away and his site wasn't being maintained anymore. I vaguely remember paying one of the broker sites to try buying the domain, but it seems there was nothing but crickets on the other end

We have been unsuccessful in furthering negotiations with the seller for domain FAHIM.CA as they have not responded after several offers for the domain.

Ok fine; so I pretty much just gave up and moved on with my life. Except, a couple of years later - just checked on a whim, and turns out the site was available now (presumably the prior registration had finally expired and was up for grabs).

Ended up registering it with 10dollar.ca (which is now rebranded as grape.ca), and just had the domain parked for years till I started this blog last week.

Probably was frustrating for any other Fahim's out there who wanted to snag the domain especially seeing I had been keeping it dormant - a couple had even reached out to purchase it (seems like they were able to get a hold of me indirectly through the Canadian Internet Registration Authority's internal message delivery service - while DNS records do have my personal information redacted, I suppose there still needs to be a way for folks to reach the registrant)

Purchase Domain
Hello, I hope you're doing well. I noticed that the domain has not had a website in the last few years and am wondering if I can purchase it from you. My name is Fahim hence why I want to buy the domain. If you would like to discuss this over the phone, please feel free to text/call/whatsapp me or email me if it's easier. Thank you!

Sorry bro, I ain't letting it go 😀

Why self-host?

Self-hosting is the practice of running and maintaining a website or service using a private web server, instead of using a service outside of someone's control. Self-hosting allows users to have more control over their data, privacy, and computing infrastructure, as well as potentially saving costs and improving skills.

I had been tempted to go with something like wordpress.com's free tier, but that would serve ads and I didn't want to deal with that. And since I have no idea if I even want to keep blogging, it made no sense for me to pay for the higher tiers. A couple of neurons in my brain got me thinking - I could host my wordpress.org site! Some more research eventually led me to Ghost - I appreciated its relative simplicity and ease. Again, I'm not interested in paying for anything (nor requiring any viewers/lurkers to pay), so went with the self-hosted route.

The biggest worry about self-hosting I've had is security - I'm opening external traffic into my internal network. As I'll eventually outline below, there are a few mitigating steps to overcome this.

A quick introduction to Unraid

Long story short, I had a spare Dell tower, and quite a hodgepodge of spare 8TB and 3TB hard disk drives, and I just wanted to start up a Plex media server I could use to consume media locally on my network and (securely) share with some close friends. Originally I'd have Plex running off my desktop machine, but eventually decided to get a dedicated device that would be up and running 24/7 from my basement.

💡
I've pretty much locked myself in using Plex - got a lifetime pass quite a few years ago, and I've been pretty content thus far. I wish Plex would focus more on the presentation aspects of local media instead of adding online content/features (some of which are frankly obscene and/or cheap); I get Plex wants to become some super-cool streaming company and maybe one day rival Netflix... but I don't care for those features and have gone out of my way to opt out and/or disable them. There are times when I'm tempted to at least start up a parallel universe where I have alternative media servers such as Emby or Jellyfin deployed, but that takes time/effort I don't have. At the end of the day, Plex serves my needs and does it pretty well.

I'm hoping I don't trigger a religious debate of Unraid versus TrueNAS (scale or core) versus just running your Ubuntu/Debian server versus getting a pre-built NAS from the likes of Synology or QNAP. I was tempted to try out TrueNAS scale (free), but at the end of the day decided that because I had a set of drives that were of different sizes/capacities, setting up a couple of ZFS vdevs was more hassle than I was willing to deal with.

FEATURE UNRAID TRUENAS
File System XFS ZFS, with built-in snapshots & RAIDZ
Performance Good, less resource-intensive Higher, but more resource-intensive
Data Protection Parity drive-based RAIDZ-based
Usability Easy, flexible More complex, advanced controls
VM Support Strong support for Docker & VMs Less emphasis on Docker & VMs, but available with TrueNAS Scale.
Hardware Requirements More flexible, less demanding More demanding, especially on larger arrays
Ideal Use Case Home servers Enterprise-grade applications

Went with UnRaid, plunked down some cash for the Plus license, and have been a very satisfied customer.

Unraid Docker Setup

💡
I'm assuming you're already familiar with Docker; if not, there are quite a few good online resources such as the official docs overview

Ensure Docker is enabled under Settings > System Settings > Docker

Next, I'd recommend enabling the Community Applications plugin. Instructions can be found here

[Plug-In] Community Applications
Community Applications (aka CA) This thread is rather long, and it is NOT necessary to read it in order to utilize Community Applications (CA) Just install the plugin, go to the apps tab and enjoy the freedom. If you find an issue with CA, then don’t bother searching for answers in this thread as…

Since I prefer using docker-compose to create stacks of related apps (instead of using Unraid's default single app view), we also need to get the docker composer manager plugin

[Plugin] Docker Compose Manager
Compose Manager Beta Release! This plugin installs docker compose and compose switch. Use “docker compose” or “docker-compose” from the command line. See https://docs.docker.com/compose/cli-command/ for additional details. Install via Community Applications This plugin now adds a very basic contr…

If you're looking for some more info on using setting it up, this is a good resource

Docker Compose for Unraid - Docker Compose

Production mode Ghost stack

Once you've got a docker-compose manager up and running, create a new stack and edit the docker-compose.yml file similar to the snippet below. The UI editor is fairly rudimentary, but it works. Command-line editing via nano/vi is fine too.

💡
This is a sample YAML file; please don't use Password123 as your actual DB password 😀You'll also need to adjust the volumes parameters as needed
version: '3.8'
services:
  ghost-db:
    image: 'mysql:8'
    container_name: ghost-db
    security_opt:
      - 'seccomp:unconfined'
    restart: always
    command: '--default-authentication-plugin=mysql_native_password'
    ports:
      - '3306:3306'
    environment:
      MYSQL_ROOT_PASSWORD: Password123
    volumes:
      - '/mnt/user/appdata/ghost/mysql:/var/lib/mysql'
  ghost-server:
    image: 'ghost:latest'
    container_name: ghost-server
    cap_add:
      - CAP_SYS_NICE
    security_opt:
      - 'seccomp:unconfined'
    restart: always
    ports:
      - '2368:2368'
    depends_on:
      - ghost-db
    environment:
      url: 'https://fahim.ca'
      database__client: mysql
      database__connection__host: ghost-db
      database__connection__user: root
      database__connection__password: Password123
      database__connection__database: ghost
    volumes:
      - '/mnt/user/appdata/ghost/content:/var/lib/ghost/content'

Once you've got the composition up - you should be able to view your site using something like

https://unraidserver:2368/ghost

The next step, of course, would be to expose your ghost instance externally to the world.

Cloudflare

There's probably more than one way to approach this, but I found the following steps to work well for me - we will be focusing on how to use Cloudflare as our DNS or Domain Name Server. Afterwards, we will take a look at some basic Cloudflare tools to help keep your website from being attacked by bots and malicious attackers.

Create a Cloudflare account

Cloudflare allows us to use their name servers so we can obfuscate our home IP. We will also have a wide variety of security tools at our disposal all for free. Register an account at Cloudflare. It's free and well worth your while. You will need to verify your email so be sure to use an email you have access to.

There are, of course, paid plans with more bells and whistles https://www.cloudflare.com/plans/

Add your site/domain to Cloudflare

Once you get logged in and verify your email, you can now add your domain to Cloudflare. Click on Add a site

And follow along the steps. I selected the free tier, of course.

Since I bought my domains from grape.ca, I will need to replace the placeholder grape.ca name servers with the provided Cloudflare name servers as seen below.

This is what it looks like on the grape.ca end of things; of course, your registrar settings and look and feel are likely to be different but similar.

Submit the changes then go back to the Cloudflare dashboard.

Now you can click the Done, check nameservers button and complete the quick start guide. It's a good idea to enable Always use HTTPS and then leave the remaining parameters at the default values for now.

You will be taken back to the name server setup page at the end of the guide. You will have to wait about 10-15 minutes depending where you live for Cloudflare to propagate the new name servers. You will get an email when it has been completed so be patient! You will eventually get this email to know your domain was successfully activated on Cloudflare. Like me, you could also periodically spam trying to visit your domain in a web browser to see if you're being redirected to Cloudflare yet.

Cloudflare Zero Trust

At this point, let's do a quick status update:

  • we have a ghost instance running on our local LAN
  • Cloudflare is now our DNS provider

What we need to now do is get Cloudflared tunnel client to redirect any traffic directed at our domain to be routed to our ghost server instance - without giving away our network coordinates, of course! To do this we will utilize the Zero Trust Access Tunnel and install a Cloudflared agent using Docker.

On your main Cloudflare dashboard, you will see a link on the left side panel called Zero Trust

The first thing we want to do is set up our access tunnel using Docker. This will allow Cloudflare to pass traffic to your domain even if your home IP address changes. This will also obfuscate your home IP address which is probably a good thing.

Click on Access on the left side to expand the menu, then click Tunnels. This will prompt you to select a plan and again, we can choose the free plan but you will still be required to add a payment option. I connected my PayPal account and it works great; I've been assured that no payments will be made and this step is a mere formality.

Once you get your payment gateway connected, you can create your first tunnel. I choose the Docker environment.

Don't share this command because this has a unique token ID that ties to your account with Cloudflare. If this Docker container is not running, your website will not load properly.

Now all that's left to do is add your Public Hostname

Choose your domain from the dropdown menu, add the Unraid host IP and port of the Ghost instance (e.g. http://unraidserver:2368) then select HTTP as the service type. Hit Save hostname and you're done... with this part 😉

Cloudflared Tunnel Client

Back in Unraid, you could

  • take the docker run command from the previous Cloudflare setup to install and run the tunnel connector
  • alternatively, go back and add it to the docker-compose stack, or
  • install the tunnel client by searching for the docker template in Unraid's app list

For the sake of variety, I'll go with the last option. When you do search for cloudflared you will be presented with a few options. I went with the CloudflaredTunnel template. Go ahead and install it

Enable Advanced View in the top right corner and look for Post Arguments and paste in your Cloudflare token after --token YourTokenHere. Click Apply.

If all went well, back in the Cloudflare Zero Trust console, under Access > Tunnels, you should see your tunnel in a HEALTHY state.

A few more Cloudflare settings

By now, you should see updated DNS records in your website's Cloudflare dashboard.

Under SSL/TLS, there are a few settings worth enabling. I went with Full encryption mode, by the way.

Under Edge Certificates you should see a free Universal SSL certificate created (this is provided by Let's Encrypt and is auto-renewed).

I enabled Always Use HTTPS

I also prefer requiring the latest TLS version. Sorry to anybody still using Internet Explorer; maybe the folks at AT&T 😏

The Grand Finale

Your site should now be available! Your IP is obfuscated and traffic is encrypted.

If you look around the Cloudflare dashboard you can also glean some traffic analytics.