Getting Started With This Site

To start with this blog, I think it might be worth talking about this specific project; the website you are currently on.

To start with the basics, the server is hosted behind Cloudflare which I rate highly even just for it's API controlled DNS functionality. The server itself is a small virtual machine in some datacenter halfway across the world. It's quite cheap and as projects go on, I might find some way to take the whole thing "serverless".

I find myself planning on what a page on my new site might contain. I imagine a "Code Snippet" might be pretty cool. A highlight of all of the Docker Compose iac or something? How do I even write a Code Block on Ghost?!

  
    I find myself trying to figure out how to write a code block. This one looks somewhat plain.
    
    Not to worry, it seems as though there's a tutorial I can follow:
    https://ghost.org/docs/tutorials/code-syntax-highlighting/
  

^ This one is just some HTML. It's not even that intuitive.

The guide above says to use 3 backticks. Similar to markdown I guess:

def hellocodeblock():
    print("Hey Look, it's a code block")
Well this is pretty

Docker Compose

With that out of the way, lets get into some Docker Compose. I generally will launch most of my projects using Docker. The reason? It's easier to clean up when I don't want it anymore and i change my mind a lot.

Lets say one day, I want to switch a service I'm running. In my example, I don't want to use Keycloak anymore because I find it too overkill for my purposes. I want to swap it out for Authentik (more on this in a later post), can this even be done easily without Docker? Apt/Yum remove? What about left over files...

For this reason, I like to put all of my code into Docker and I use Docker Compose for the deployment. For this site, here is the code:

---
version: '3.1'

services:

  ghost:
    container_name: ghost
    image: ghost:4-alpine
    restart: unless-stopped
    ports:
      - 3008:2368
    volumes:
      - /opt/docker/ghost:/var/lib/ghost/content
    environment:
      database__client: mysql
      database__connection__host: mariadb
      database__connection__user: DATABASE_USERNAME
      database__connection__password: DATABASE_PASSWORD
      database__connection__database: DATABASE_NAME
      mail__transport: SMTP
      mail__options__host: MAILSERVER_IP
      mail__options__port: 25
      mail__options__auth__user: EMAIL_USERNAME
      mail__options__auth__pass: EMAIL_PASSWORD
      mail__from: Dan Jackson Ghost <EMAIL_ADDRESS>
      url: https://danjackson.me
    networks:
      - MySQL_Connect
      - frontend
networks:
  MySQL_Connect:
    external: true
  frontend:
    external: true

You might notice above that I've created a bridge network called frontend and you might wonder why. The answers to that question are found here

I've got another network called MySQL_Connect. This is specifically to hold the connection to the MySQL database. The network is specified as internal only and is used to allow traffic to transmit between a single database instance on the Docker host. The MySQL container is not connected to any externally facing Docker network by design and therefore can only be accessed by other containers for additional security.

Reverse Proxy

Infront of every good HTTP based Docker system, there is some sort of reverse proxy to direct the traffic. In my case, despite years of using Nginx, I've now switched to Caddy 2 for all of my traffic.

Why?

Painlessly easy for automated SSL.

Don't get me wrong, you can use both Certbot and Acme.sh for SSL certificates on Nginx. Both solutions support DNS validation too. These are both fairly easy but they both lack a certain instant automation. On Caddy 2 however, it couldn't be more easy. You simply add your Cloudflare API key to the Caddyfile and then define a domain

Here is that config:

{
    # email to use on Let's Encrypt
    email EMAILADDRESS

    acme_dns cloudflare SUPERSECRETAPIKEY
}
### SNIPPITS ###
(webconf) {
  encode gzip
}

(theheaders) {
    header_up X-Forwarded-Ssl on
    header_up Host {host}
    header_up X-Real-IP {remote}
    header_up X-Forwarded-For {remote_host}
    header_up X-Forwarded-Port {server_port}
    header_up X-Forwarded-Proto {scheme}
    header_up X-Url-Scheme {scheme}
    header_up X-Forwarded-Host {host}
}
danjackson.me {
   reverse_proxy http://127.0.0.1:3008 {
      import theheaders
    }
import webconf
}
Caddy file syntax doesn't get fancy highlighting it seems :(

...I probably don't even need the headers bit.

That's the basis of this Ghost installation. Following the above, I simply point ensure that Cloudflare is pointing at the right server.

Improvements

If you stumble across this and think you want to try this yourself, there are a couple of things with this that can be fixed. For example:

reverse_proxy http://127.0.0.1:3008

There's nothing wrong with this Caddy code, however since both Caddy and Ghost are both in the frontend network, I don't need to open the port forwarding on the host AND I don't need to specify an IP address here, the container name would simply be enough.

Until next time.