Create your own self-hosted blog today!

Share
Create your own self-hosted blog today!
Photo by Thought Catalog / Unsplash

In my journey into the tech and computer science world, I often thought it could be nice to have a centralized way to document my progress. When you spend countless hours trying to solve a weird bug, when you finally find the solution, you don't want to forget it. And someone else could also actually find it useful.

This is why I decided to create this website. The goal is to share my what I find useful, the little side projects I do (and the progress of the bigger ones), to document the issues I encounter on my homelab and the solutions I found, and so on. I want to keep it high level enough so that anyone related to the tech world can understand it, but still detailed so that it is useful and that if someone is interested or encounters the same issue, they can still reproduce my work.

On this first post, I will show you how I setup this website and how you can do the same!

What is Ghost?

As you can see by scrolling down this page, the blog website is powered by Ghost. It is a blogging platform that allows to seamlessly manage content, posts, users, etc, even without being a developer.

An alternative way I could have setup this website, is simply to manually write the static HTML files and serve them using NGINX or any other web server. However, as you can think, this restricts the functionalities, in particular the interactivity, and it forces me to write HTML content in a file editor and then manually upload the content on the server. Other solutions exists to serve Markdown content instead of HTML but I still cannot write my posts in my browser.

I could also have developed my own CMS (Content Management System), but I would have spent many hours to create something that isn't better than something that already exists.

Ghost can be self-hosted, it is free, open-source, the installation is quite simple and it allows the management of the content in a very easy way. You can customize it with themes, users can register, add comments, you can add other authors etc. This is why I chose it for my blog.

Installation using Docker

Since I use containers for every other service I host on my server, of course I tried to install Ghost as a Docker container. Fortunately, a ready-to-use compose file with installation instructions is provided as a preview. I based my setup on this, with some adaptations.

Reverse proxy and DNS setup

First, the provided Docker compose setup adds a dedicated Caddy web server, specifically for the blog. However, on my server, I already have a NPM (NGINX Proxy Manager) reverse proxy that listens on 80/443 ports. So I removed Caddy from the compose file and forwarded the port Ghost listens on (2368). If you don't have any other web services on your server, you can keep it as is. Otherwise, you can add an additional entry on your reverse proxy to forward traffic from your dedicated domain to the specified port. In my case, it is https://blog.ylked.ch. Don't forget to add a CNAME/A record at your DNS provider.

If you configure the url variable to https:// [...], don't forget to setup inside NPM the use of a TLS certificate.

Mail provider setup

To allow secure registration, login, password reset, etc, you also need an e-mail provider. Official documentation proposes to use Mailgun, but since I've already used Brevo for a previous project, I decided to reuse it. You can send up to 300 e-mails per day for free.

The main thing you'll have to do is to authenticate your domain (if you want to use a custom domain). It consists of adding specific DNS records for your domain to prove that you own it. The instructions given by Brevo are pretty straightforward and I didn't encounter any issues with it. After that, you can get an SMTP key, alongside with the server address, login and port, which is all you need to send an e-mail!

Save those credentials and set them up in the environment variable file .env, under the following keys:

mail__transport=SMTP
mail__options__host=<your smtp host>
mail__options__port=465
mail__options__secure=true
mail__options__auth__user=<your login>
mail__options__auth__pass=<your smtp key>
mail__from="My Blog <blog@example.com>"

If you try to sign in on a private navigation tab, you should receive a six-digit code by e-mail to login.

Enable analytics

Ghost also comes with a way to analyze the traffic on your blog, i.e. how many visitors, on which articles, where do they come from, how many of them did create an account, etc, which could be useful. It uses Tinybird to track everything. Simply following the official guide almost worked for me: since I use a specific setup with NPM (and not the provided Caddy configuration), I had to do one little adaptation. In the initial setup, all traffic to the blog URL (blog.ylked.ch) is redirected to Ghost container. However, for the analytics to work, the requests to <blog URL>/.ghost/analytics must be forwarded (and rewritten) to the analytics container. So, for it to work correctly, I had to add the following configuration in the advanced section of the NPM proxy host.

location ~ ^.*/\.ghost/analytics {
    rewrite ^.*/\.ghost/analytics(.*)$ $1 break;
    
    proxy_pass http://<ip-address>:<port>;
    
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Replace <ip-address> and <port> with your setup. The port is specified in the compose file (3000 by default) and the IP is the one from your computer. However, localhost won't work if both services (reverse proxy and analytics) live in containers. In my case, I have a DNS record pointing to my server private IP address configured in my home DNS server. That being said, the best way to do it is to make both containers share the same Docker network, and simply use the name of the service, e.g. http://analytics-service:3000.

Conclusion

It took some trials and errors to make Ghost work correctly with my own specific setup, but did succeed at the end. Now, I can write my own blog posts that serve simultaneously as an internal documentation for future reference and also to interest others. When I try new projects for my homelab, I will be able to simply track and document everything directly in my browser, without having to write markdown notes that get lost or write raw HTML files (and forget to upload them).

If you're setting up Ghost, and get stuck on an issue, don't hesitate to leave a comment or to contact me, I will gladly help you!