I wanted to start a blog and here we are. This blog you are reading right now is using Ghost and is completely self-hosted. The process was quite painless but there were a few hiccups along the way.
In this post, I will be going through the steps that I did to get Ghost up and running. My existing infrastructure includes an Nginx reverse proxy and a dynamic IP that I get from my ISP.
Ghost has a great guide on installing Ghost on Ubuntu. The only thing is that I like RHEL and dislike Ubuntu. So I set up an RHEL 8 VM and followed the instructions.
First, we are tasked with installing Node. Ghost does not support node v16.x yet so let's install node v14.x from NodeSource:
curl -fsSL https://rpm.nodesource.com/setup_14.x | sudo bash -
Then I installed the MySQL server with
sudo yum install @mysql
After that, I ran mysql_secure_installation
.
Installing Ghost CLI and then installing Ghost itself was quite pleasant with the help of the guide. The Ghost CLI setup does not detect Nginx correctly when using a non-Debian distro so I had to do that myself. The Nginx configuration used by Ghost CLI is described here.
Now let's talk a bit about my network setup. My server resides in my house and as such, I don't have a static IP from my ISP. I've been using Duck DNS as my dynamic DNS for a while now.
I have a couple of subdomains that are pointed towards Duck DNS via CNAME
records but using a CNAME
record at the root of the domain is prohibited.
Gratefully my DNS provider offers a custom record called ALIAS
. This functions like a CNAME
but in the background, the DNS provider goes through the DNS tree until it encounters an IP.
Now that I had a domain pointing at the right IP I just needed to proxy the traffic to Ghost. My gateway has been configured to forward 80 and 443 to another VM that I use to host a couple of other HTTP Projects. But for Ghost, I had made a completely new VM.
I think it could have been possible to expose Ghost to the network by editing the host
parameter inside config.production.json
but as I think I'll be adding other web services to this VM I decided to configure Nginx to act as a reverse proxy and then chain that to the "main" Nginx reverse proxy that I have forwarded from my gateway.
I started with making a new configuration onto the Main Proxy.
server {
server_name serverspike.io;
listen 80;
location / {
proxy_set_header Host $host;
proxy_pass http://192.168.1.13/;
proxy_redirect off;
}
}
The next step was to run certbot. Certbot modifies the configuration file somewhat but it should now work with HTTPS traffic. I tested this by running http-server on 192.168.1.13 and it worked! Now we just needed to configure the Ghost VM's Nginx instance.
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:2368;
}
client_max_body_size 50m;
The most important line here is: proxy_set_header X-Forwarded-Proto https;
If that header is omitted or set to $scheme then Ghost thinks that we are running on bare HTTP and it proceeds to go into a redirect loop. With my current setup, only the outbound proxy uses HTTPS. The traffic between the proxies and between the inner proxy and Ghost is unencrypted.
In the future, I may also secure the traffic between the proxies so that the main proxy decrypts and then re-encrypts the data before sending it.
This works! Almost. For some reason Ghost had decided to change its port to 2369 and I had no idea until I ran netstat -tulpn
. One could change the port in config.production.json
but I chose to edit it on the Nginx side. After restarting Nginx everything worked!
Setting up Ghost was a pleasure and I'm looking forward to seeing what it can provide. Cheers!