So you’ve stumbled across this post in your search to self-host a blog, such as this one - using open source software. While there are many ways to do so, through this series I plan to share with you the road I took and what I settled on (for now). As you go through these posts, keep in mind that the world is your oyster! You do not need to do things exactly like this and if you find better ways or cool stuff along the way, please do share!

Note: This series will assume you have a decent technical understanding of Linux, containers, and the way the internet works. If anything is unclear, please reach out and I will add clarity or try to help.

The Technology Stack

I will try to create a diagram in the future, but the basic idea of what will be used in this series is as follows:

  1. A domain and DNS management
  2. A host with a public IP and a little storage (could be a physical host of your own or something like a VPS). For reference on the storage, my blog files are currently sitting at a staggering 11MB. Obvoiusly, that could grow substantially depending on what you host but you get the idea.
  3. A Linux OS on the host will be assumed (in my case, it is Fedora)
  4. Podman for container creation and management (should be easily modifiable if using Docker)
  5. Jekyll Docker container
  6. Beautiful Jekyll theme (although there are many themes out there)
  7. Traefik container for reverse proxy and encryption duties

Domain Name Registration

The first thing you’ll need to think about is a domain name. There are many registrars out there, but I settled on Cloudflare for this one based on various research in trying to find the best and cheapest yet reliable registrar of today. In past lives, I used GoDaddy coupled with Zoneedit and that always worked great, so it’s up to you - there are a lot of options.

Moving forward, this series will specifically reference Cloudflare but can be modified for other registrars and DNS management tools/systems. I will not go into the details of registering your domain, but you can head over to this article for details and getting started with Cloudflare’s registrar. In a nutshell, you’ll need to sign up for an account (free), head over to your admin panel (where you’ll land upon login), expand Domain Registration in the navigation, and follow the process under the Register Domain.

DNS Configuration

Now that you have your domain, note your server’s public IP address and create an A record for your domain referring back to your IP. For Cloudflare, this is done via the admin panel under DNS/Records. You can add additional A or CNAME records should you want to use a subdomain for your blog.

Note: If using Cloudflare, you can use the DNS Only or Proxied setting for your records given that these are static pages. If you would like to keep your IP address private, go for Proxied. More info on these settings can be found here.

Dealing with Dynamic IP Addresses

If your host’s public IP address is dynamic, you will need to determine how best to keep your DNS records current as your IP address changes. If you have a static address, this should be a non-issue.

As my host indeed uses a dynamic IP address, I decided to use a cron job (every five minutes) to execute a script that updates the A record via the Cloudflare API if it changes. If you don’t know much about cron, you can find a good primer here.

As for my script (bash), here is a sanitized version for reference:


ZONEID=$(curl -s -X GET "" \
  -H "X-Auth-Email:" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" | grep id | cut -d'"' -f6)

DNSRECORDID=$(curl -s -X GET "$ZONEID/dns_records?type=A&" \
  -H "X-Auth-Email:" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" | grep id | cut -d'"' -f6)

CURRENTDNSIP=$(curl -s -X GET "$ZONEID/dns_records?type=A&" \
  -H "X-Auth-Email:" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" | grep id | cut -d'"' -f26)

if [[ "$LOCALIP" == "$CURRENTDNSIP" ]]; then
curl --request PUT \
  --url$ZONEID/dns_records/$DNSRECORDID \
  --header 'Content-Type: application/json' \
  --header 'X-Auth-Email:' \
  --header 'Authorization: Bearer <token>' \
  --data '{
  "content": "'"$LOCALIP"'",
  "name": "",
  "proxied": false,
  "type": "A",
  "comment": "Domain verification record",
  "ttl": 3600

Script Callouts

  1. This script currently relies on (yes, really) and if that site is unavailable, this script will fail. It is recommended to use a more reliable method to procure your IP address :)

Update: I have moved to a solution of hitting my router directly via ssh and grabbing the Public IP. This will be more reliable, but unique for each environment.

  1. in the URLs should be your domain.
  2. X-Auth-Email is the email (login) associated with the Cloudflare account managing your blog domain.
  3. The Bearer <token> is created via the Cloudfare admin panel and needs the DNS:Edit permission for DNS zone. Details can be found here.


Assuming your domain name lookups are resolving to the correct place - either directly to your public IP or proxied through something like Cloudflare - you should be good to go!

Example (proxied through Cloudflare):

$ dig +short

In part 2, we will discuss using Podman to run the Jekyll container. Stay tuned!