Select Page

Create your own blog!

 This guide it meant for anybody to get their own blog up and running. I am trained in software development,  if you have any questions please post in the comments and I will address accordingly! I’ve provided additional information to help guide you in creating the setup via links throughout the text. After completion of this guide, you can add all sorts of enhancements, or keep your site as is for development purposes. This should get you up and running with a new blog in less than a day!

Guide

Create Your Own Blog Using Caddy, Docker and Ghost

Purchase a VPS

For this part, you will need to spend some money unless you choose to partake in a free trial from a cloud provider. Another option, though far less likely, is to host your site on your own machine.
I went with Digital Ocean for their price point and ease of use. There are a multitude of offerings and I encourage you to do your own research to find out what works best for your needs. I am using Ubuntu, though this guide should still be helpful if you’re running on other Linux OS’s.
With this, I selected the cheapest Virtual Private Server (VPS) and setup SSH authentication using the Secure your server guide below. From there I took additional steps to secure my server adding fail2ban and installed docker under a non-root user the guide for this is also below.
Secure your server
Install Docker on Ubuntu

Setup Caddy

For all Docker volume information, I am using /data and for Docker containers I am working from /opt/docker-containers/ – this is my own preference. For more information on Docker volumes, review this documentation.
Adding our custom routing for caddy, based off Caddyfile documentation

/data/caddy/Caddyfile

Now we need to map this domain name to our VPS.

{

# Email for lets encrypt

email [email protected]

}

example.com {

reverse_proxy ghost:2368

}

www.example.com, blog.example.com, www.blog.example.com {

redir https://example.com{uri}

}

Map VPS domain name

Digital Ocean provides documentation on how to do this. Whatever VPS provider you choose should have guidance for this section though.
The steps I took are as follows:

  • Map my Domain provider’s (e.g. – GoDaddy, Namecheap, etc.) DNS nameservers to ns1.digitalocean.com, ns2.digitalocean.com and ns3.digitalocean.com (Note: This may be different based on the services you’re using)
  • A name mapping example.com to the server’s IPv4 address
  • AAAA name mapping example.com to IPv6
  • CNAME mapping www.example.com, www.blog.example.com, blog.example.com to example.com
    Caddy will perform the routing once we reach our server

Setup Docker-Compose files

Lastly, we need to setup and run our services. So far we’ve only laid the ground work. Ghost and Caddy aren’t doing anything at this point.
To get more insights into docker-compose and what we’re doing below, I highly encourage you to review the following documentation.
We need to setup a network for our components to run on, let’s call it “web”.

Do this via command line within your project’s directory

$> cd /opt/docker-containers/myblog

$> docker network create web

#verify network is created:

$> docker network ls

I want to give a big shout out to Brian Burroughs who provides great documentation on a development setup of Ghost and Caddy, here.
As you can see by the volume definitions in docker-compose.yml we will need the following directories:

/data/caddy/

/data/caddy/data

/data/caddy/config

/data/ghost/

/data/mysql/

In your file…

/opt/docker-containers/myblog/docker-compose.yml

Enter:

version: "3"
networks:
 web:
 external: true
services:
 caddy:
  image: caddy:2-alpine
  container_name: atoz-caddy
  restart: unless-stopped
  depends_on:
   - ghost
  ports:
   - "80:80"
   - "443:443"
  volumes:
   - /data/caddy/Caddyfile:/etc/caddy/Caddyfile
   - /data/caddy/data:/data
   - /data/caddy/config:/config
  networks:
   - web
 ghost:
  image: ghost:4.11-alpine
  container_name: atoz-blog
  restart: unless-stopped
  depends_on:
   - db
  ports:
   - 2368:2368
  environment:
  # see https://ghost.org/docs/config/#configuration-options
   database__client: mysql
   database__connection__host: db
   database__connection__user: ${MYSQL_USER}
   database__connection__password: ${MYSQL_PASSWORD}
   database__connection__database: ${MYSQL_DATABASE}
   url: https://aytuzi.com
  volumes:
   - /data/ghost:/var/lib/ghost/content
  networks:
   - web
 db:
  image: mysql:8
  container_name: atoz-db
  command: mysqld --default-authentication-plugin=mysql_native_password
  restart: unless-stopped
  ports:
   - 3307:3306
  environment:
  # see https://hub.docker.com/_/mysql
   MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
   MYSQL_DATABASE: ${MYSQL_DATABASE}
   MYSQL_USER: ${MYSQL_USER}
   MYSQL_PASSWORD: ${MYSQL_PASSWORD}
  volumes:
   - /data/mysql:/var/lib/mysql
  networks:
   - web

To keep our passwords and other login information outside of the docker-compose file, I am creating a .env file. This information is injected to docker-compose using the ${} operator.
Example
/opt/docker-containers/atoz/.env

MYSQL_ROOT_PASSWORD=somepassword
MYSQL_DATABASE=databasename
MYSQL_USER=username
MYSQL_PASSWORD=someotherpassword

Start your containers

$> docker-compose up -d
#if no errors, trail your logs
$> docker-compose logs -f
#verify the configuration from .env are present
$> docker-compose config

If no issues are present, go to your domain. With this setup, you can visit your site under all of the definitions you have set.

Additional Enhancement Opportunities

There are plenty of enhancements that can be made to this setup, including but not limited to:

  • Enhanced logging, monitoring and regular system backups
  • Security updates
  • Creation of a pipeline to automate updates to our system