Securing your blog with Traefik and LetsEncrypt

This post demonstrates how to use Traefik and LetsEncrypt to obtain a free TLS certificate. Traefik enables us to make the blog accessible securely on the public internet, even if it is running on a private network.

Securing your blog with Traefik and LetsEncrypt

I recently launched my blog, which is powered by Ghost and Docker. I wrote a post about it; you can read it here:

Starting your blog with Ghost and Docker | iThink
Setting up a new blog is a breeze with Ghost and Docker.

If you have not set it up then make sure you read the post first and set up your blog. Today I will be extending that post to add two new features to the blog.

  1. To make the blog visible on the public internet
  2. To make sure the blog is secure with a TLS certificate

We need a domain name for our blog where we want our blog to be accessible. We can purchase one from a provider such as Name.com or NameCheap.com. Alternatively, we can sign up for a free dynamic DNS provider such as NoIP.com. Any of these services will work, as long as we know how to set it up.

We will be using Traefik as a reverse proxy for the blog. Traefik can also help us get a free LetsEncrypt TLS certificate, which will help create an encrypted connection between our website and the reader's browser. This ensures that the communication between the two is secure and private. A secure connection is indicated by a lock icon next to the address bar in the browser, giving readers the assurance that their data is safe and secure. This is especially important in today's digital landscape, and having a trusted certificate is a great way to be sure our readers have the best possible experience.

To achieve this, let's extend the docker-compose.yaml file to add these features.

version: "3"
services:
  ghost_database:
    image: mysql:8-debian
    container_name: ghost_database
    restart: unless-stopped
    volumes:
      - type: bind
        source: /path/to/database
        target: /var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=password
  ghost:
    image: ghost:5-alpine
    container_name: ghost
    restart: unless-stopped
    depends_on:
      - ghost_database
    volumes:
      - type: bind
        source: /path/to/content
        target: /var/lib/ghost/content
    environment:
      database__client: mysql
      database__connection__host: ghost_database
      database__connection__user: root
      database__connection__password: password
      database__connection__database: ghost
      url: https://your.domain.com
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.blog.rule=Host(`your.domain.com`)"
      - "traefik.http.routers.blog.entrypoints=websecure"
      - "traefik.http.routers.blog.tls.certresolver=myresolver"
      - "traefik.http.services.blog.loadbalancer.server.port=2368"
  traefik:
    image: "traefik:v2.9"
    container_name: "traefik"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "[email protected]"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - type: bind
        source: /path/to/letsencrypt
        target: /letsencrypt
      - type: bind
        source: /var/run/docker.sock
        target: /var/run/docker.sock
        read_only: true

This is the complete docker-compose.yaml file you will need. As you can see, we have added another service in our configuration file that uses Traefik. Traefik is exposing two ports: 443 and 80. Port 80 is used by the HTTP protocol, which is not secure. Port 443 is used by HTTPS, which is secure. To use HTTPS, we need a TLS certificate. To store the certificate, create a new directory named letsencrypt and add it to the volumes in docker-compose.yaml under the new service we created.

To configure the domain name, find the IP address of the computer where you want to host your blog. If you have a dynamic IP, you can use a dynamic DNS service, which I mentioned before. Set the IP address of the computer in your DNS provider with the domain name that will be used to access your blog. For example, if your domain is your.domain.com, set the IP address against your domain name. For more information, contact your domain name provider or your dynamic DNS service provider.

To configure the domain and TLS certificate for your domain name, add the following labels to the Ghost service.

...
labels:
    - "traefik.enable=true"
    - "traefik.http.routers.blog.rule=Host(`your.domain.com`)"
    - "traefik.http.routers.blog.entrypoints=websecure"
    - "traefik.http.routers.blog.tls.certresolver=myresolver"
    - "traefik.http.services.blog.loadbalancer.server.port=2368"
...

Make sure you replace the domain (your.domain.com) with a working one in both the ghost service url environment variable and in labels for Traefik service.

Run docker-compose up -d in your terminal to start all three services. If there are no errors, Traefik will get a TLS certificate for your domain name and make your blog accessible securely when you browse it.

Navigate to https://your.domain.com (note the https) in your browser. You will see a lock icon next to your address bar, confirming your connection is secure.

💡
Tip: If your computer is behind a router, you may need to add a port forward rule. All routers are different, so consult your router's help page for instructions. You must forward ports 80 and 443 to the computer where Ghost and Traefik are running.

Now your ghost blog is running securely and is publicly available. You can share it with your friends and family and invite them to sign up and read your blog.

ℹ️
Running securely with TLS does not guarantee that your blog is hack-proof. There are many other ways it can be compromised, such as using an insecure password. TLS merely eliminates one potential avenue for hackers.