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!


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


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


# Email for lets encrypt

email [email protected]

} {

reverse_proxy ghost:2368

},, {



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, and (Note: This may be different based on the services you’re using)
  • A name mapping to the server’s IPv4 address
  • AAAA name mapping to IPv6
  • CNAME mapping,, to
    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:






In your file…



version: "3"
 external: true
  image: caddy:2-alpine
  container_name: atoz-caddy
  restart: unless-stopped
   - ghost
   - "80:80"
   - "443:443"
   - /data/caddy/Caddyfile:/etc/caddy/Caddyfile
   - /data/caddy/data:/data
   - /data/caddy/config:/config
   - web
  image: ghost:4.11-alpine
  container_name: atoz-blog
  restart: unless-stopped
   - db
   - 2368:2368
  # see
   database__client: mysql
   database__connection__host: db
   database__connection__user: ${MYSQL_USER}
   database__connection__password: ${MYSQL_PASSWORD}
   database__connection__database: ${MYSQL_DATABASE}
   - /data/ghost:/var/lib/ghost/content
   - web
  image: mysql:8
  container_name: atoz-db
  command: mysqld --default-authentication-plugin=mysql_native_password
  restart: unless-stopped
   - 3307:3306
  # see
   - /data/mysql:/var/lib/mysql
   - 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.


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