What better way to start out a tech blog than by writing out my process on how I set the blog up? By day, I am an Android developer, so this was a bit out of my comfort zone.
Although I have set up a few Wordpress blogs, this was several years ago (pre-Docker), and I was never fond of the amount of effort required to make the aesthetics decent. Ghost looks nice out of the box, and they offer a (limited) selection of free themes. Also, writing articles feels very similar to writing Medium articles, which is a pleasant experience in my opinion.
To set up the blog, I knew I wanted to use Docker. As an Android developer, I don’t use Docker as often as, say, some back-end developers; however, the benefits of constraining your application to a container are plentiful. In essence, when modifying with a self-contained application, I don’t have to worry about impacting other running containers. Therefore, modularization and separation of concerns is a must.
Prerequisites
- Virtual Private Server (VPS) – I use Vultr
- Domain name (optional, but highly recommended as you’d have to access the blog via the VPS IP address)
- Comfortable with the command-line and SSH
- Have Docker installed on VPS
Initial Steps
Point your domain to the VPS IP address (obtained from the VPS provider) by providing the following DNS records to your domain provider:
Type | Host | Value |
---|---|---|
A Record | @ | {vps_ip_address} |
A Record | www | {vps_ip_address} |
Define Docker Compose
In Docker, you can run commands to create, download, run Docker containers. Additionally, you can define a docker-compose.yml file in order to define multiple docker containers with their respective configurations. In my case, I used the following configuration:
version: '2'
services:
proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- /etc/nginx/vhost.d
- /usr/share/nginx/html
- ./docker/certs:/etc/nginx/certs:ro
ssl-companion:
image: jrcs/letsencrypt-nginx-proxy-companion:latest
container_name: ssl-companion
volumes:
- ./docker/certs:/etc/nginx/certs:rw
- /var/run/docker.sock:/var/run/docker.sock:ro
volumes_from:
- proxy
depends_on:
- proxy
danherrera:
image: ghost:2.16
container_name: ghost-dh
volumes:
- /home/danilo/volumes/ghost-dh
environment:
- url=https://www.danherrera.dev
- VIRTUAL_HOST=www.danherrera.dev,danherrera.dev
- VIRTUAL_PORT=2369
- LETSENCRYPT_HOST=www.danherrera.dev,danherrera.dev
- LETSENCRYPT_EMAIL=danilo@agileninja.io
This configuration defines 3 containers: nginx-proxy
, ssl-companion
, and ghost-dh
.
The nginx-proxy
container is responsible for routing traffic to the respective docker container that defines the VIRTUAL_HOST
and VIRTUAL_PORT
environment variables. In the above example, the danherrera container definition defines the VIRTUAL_HOST
as www.danherrera.dev
and danherrera.dev
, meaning that when a user accesses the IP address of the VPS via one of these domains, the nginx-proxy
container (running nginx) will route traffic to the ghost-dh
container.
The ssl-companion
container uses nginx’s LetsEncrypt SSL companion to automatically provide and manage an SSL certificate to the respective docker container that defines the LETSENCRYPT_HOST
and LETSENCRYPT_EMAIL
environment variables. In the above example, an SSL certificate will be provided for both www.danherrera.dev
and danherrera.dev
.
The ghost-dh
container uses the official ghost docker image (version 2.16) to define the Ghost blog.
How do you run it?
To run the docker-compose.yml configuration, execute the following from the command line from within the same directory:
docker-compose up
The -d flag means the command will be executed in detached mode and you are able to run other commands.
Once the containers are running, you can test that the domain is indeed pointing to your newly created Ghost blog container!
To see all containers: docker ps -a
To stop a container: docker stop {container_names}
To run a container: docker start {container_names}
To delete a container (must be stopped): docker rm {container_names}
Thanks to my colleague Neal Sanche for entertaining a discussion on Ghost and managed to help refine the configuration in this article.
Thanks for stopping by!