Start A Blogging Website Using Ghost and Secure It With Traefik
In this article, I will show how you can easily start a blogging website like mine – Narasimman Tech using Ghost and secure it using Traefik on a Docker Container.
Ghost is a very popular open-source blogging platform. It is an alternative to WordPress and has many inbuilt features like native SEO, Membership, Paid Subscriptions, and more whereas on WordPress they are paid plugins.
We will also use another software called Traefik. It is also a popular open-source cloud-native application proxy, API Gateway, Edge-router, and more. We use Traefik to secure our website using an SSL certificate obtained from LetsEncrypt. Once deployed, Traefik can automatically manage your certificates and renewals.
If you are a software developer or working in any tech industry, you probably might have heard of Docker. Docker is an open platform for developing, shipping, and running applications. With Docker, you can manage your infrastructure in the same ways you manage your applications.
1. Getting a Domain Name
The internet is rich with opportunities to create a business or blog on free or paid to host sites or social media. It’s not necessary to have your domain to get started, you can use the IP address. But I strongly recommend you buy a domain. Having a domain name, especially with the very familiar .com extension, can help your audience remember your website, and be easily searchable. Get a cheap domain at NameCheap. Use this link to purchase a .com domain for just $0.99!
Once you get a domain, use Cloudflare to set up your DNS. Here’s the official article:
2. Setting up your Cloud Instance
It is easy to use Ghost itself to host your website, which starts from $9 per month. But self-hosting on your system or cloud providers like AWS, Digital Ocean, Linode, etc., is cheaper, and you get full control of your data. I use DigitalOcean to host my website - Narasimman Tech, it is easy and cheap to set up. I recommend you to use DigitalOcean as well. It’s just $5 to launch a Droplet, you can use the link below to get a $100 credit.
To create a Droplet on DigitalOcean and install Docker, take a look at the official blog post that I’ve linked at the bottom of this page in the Additional Resource and Reference section or you can watch my video below.
Before you start to create a DigitalOcean Droplet, set up SSH keys. I will add a link to how to can set up SSH Keys. Refer to the Additional Resources and References below.
3. Setting up Docker
- After setting up Docker on your new DigitalOcean Droplet, copy the public IP and ssh into it as the root user from the terminal.
ssh root@<IP address of the Droplet>
- We will be running our services in a Docker Swarm Environment. If you don’t know what is Docker Swarm, check the link below.
- To start a Docker Swarm Environment run,
docker swarm init
This creates a new Swarm Environment and this becomes your manager node. You can add a new Droplet as a worker node, to scale up your services, but that’s beyond the scope of this tutorial.
Creating Required Configuration Files and Directories
- Create a folder called my website or anything you want and change the directory to the newly-created directory.
mkdir website
cd website
- We have to create a couple of files and directories to store Traefik configuration files and our SSL keys:
– Create a new directory called ‘data’ and change the directory into it.
mkdir data
cd data
- Inside this directory, create two new files called traefik.yml and acme.json change the permission of acme.json to 600.
touch traefik.yml acme.json
chmod 600 acme.json
- Open the file using any editor. Note: My favorite editor is Vim.
nano traefik.yml
- Paste the following code in the traefik.yml file.
api:
dashboard: true
debug: true
serversTransport:
insecureSkipVerify: true
entryPoints:
web:
address: :80
http:
redirections:
entryPoint:
to: websecure
websecure:
address: :443
http:
middlewares:
- secureHeaders@file
- nofloc@file
tls:
certResolver: letsencrypt
domains:
- main: yourdomain.com
sans:
- "*.yourdomain.com"
pilot:
dashboard: false
providers:
docker:
swarmMode: true
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
filename: /configurations/dynamic.yml
certificatesResolvers:
letsencrypt:
acme:
#caServer: https://acme-staging-v02.api.letsencrypt.org/directory
email: youemail@email.com
storage: acme.json
keyType: EC384
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
We have to change a few variables,
- Change yourdomain.com to the domain you own.
- main: yourdomain.com
sans:
- "*.yourdomain.com"
- Create a new directory called configurations and change the directory into it. Inside this directory, create a new file called ‘dynamic.yml’ and copy-paste the following lines.
mkdir configurations
cd configurations
touch dynamic.yml
nano dynamic.yml
# Dynamic configuration
http:
middlewares:
nofloc:
headers:
customResponseHeaders:
Permissions-Policy: "interest-cohort=()"
secureHeaders:
headers:
sslRedirect: true
forceSTSHeader: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
# UserName : admin
# Password : qwer
user-auth:
basicAuth:
users:
- "admin:$apr1$tm53ra6x$FntXd6jcvxYM/YH0P2hcc1"
tls:
options:
default:
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
minVersion: VersionTLS12
Note:
- The default username is ’admin’ and the password is ‘qwer’
We are close to launching our new Site!!
Setting up Traefik and Ghost
- Now go back to our main directory, i.e., ‘website’ directory in my case, which we created at first.
cd ~/website
- Now create a file called docker-compose.yml
- Use any editor and open the docker-compose.yml file.
touch docker-compose.yml
nano
- Paste the following in the editor you opened.
# Traefik, Ghost, and MySQL
version: '3.3'
services:
traefik:
image: traefik:latest
networks:
- traefik
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro
- ./data/configurations:/configurations
environment:
- CF_API_EMAIL=
- CF_DNS_API_TOKEN=
deploy:
mode: replicated
replicas: 1
placement:
constraints: [node.role == manager]
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.traefik-secure.entrypoints=websecure"
- "traefik.http.routers.traefik-secure.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.traefik-secure.service=api@internal"
- "traefik.http.services.traefik-secure.loadbalancer.server.port=8080"
ghost:
image: ghost:4-alpine
depends_on:
- mysql
- traefik
networks:
- traefik
- backend
volumes:
- ghost_data:/var/lib/ghost
environment:
# see https://ghost.org/docs/config/#configuration-options
database__client: mysql
database__connection__host: mysql
database__connection__user: root
database__connection__password: secretpassword
database__connection__database: ghost
# this url value is just an example, and is likely wrong for your environment!
url: https://yourdomain.com
# contrary to the default mentioned in the linked documentation, this image defaults to NODE_ENV=production (so development mode needs to be explicitly specified if desired)
#NODE_ENV: development
deploy:
mode: replicated
replicas: 1
placement:
constraints: [node.role == manager]
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.ghost-secure.entrypoints=websecure"
- "traefik.http.routers.ghost-secure.rule=Host(`yourdomain.com`)"
- "traefik.http.routers.ghost-secure.service=ghost"
- "traefik.http.services.ghost.loadbalancer.server.port=2368"
mysql:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: secretpassword
networks:
- backend
volumes:
- sql_data:/var/lib/mysql
deploy:
placement:
constraints: [node.role == manager]
networks:
traefik:
external: true
backend:
external: true
volumes:
ghost_data:
external: true
sql_data:
external: true
- Don’t close the file yet, we have to change a few parameters.
Change the Following:
- Replace ‘traefik.yourdomain.com; with a subdomain.
- "traefik.http.routers.traefik-secure.rule=Host(`traefik.yourdomain.com`)"
- Replace ‘secretpassword’ with a new password.
database__connection__password: secretpassword
- Replace ‘https://yourdomain.com' with your URL.
url: https://yourdomain.com
- Replace ‘yourdomain.com’ with a subdomain, or use the full domain.
- "traefik.http.routers.ghost-secure.rule=Host(`yourdomain.com`)"
- Finally, replace ‘secretpassword’ with the password you set above on point two.
MYSQL_ROOT_PASSWORD: secretpassword
Creating and Starting The Services
Now everything is in place! Check your files if everything is in place and the variables are modified to your requirements.
Your file structure should be like this,
<Image of the file structure>
- Now from this directory, run the command, you can change ‘site’ to anything you want.
docker stack deploy -c docker-compose.yml site
This takes a couple of a while to download the docker images and to get the SSL keys.
To list out the running services, run:
docker service ls
This lists the running services. If the REPLICAS are 0/1 wait for a few minutes, it might be preparing.
To check the status of individual services, run:
docker service ps <service name>
Replace <service name> with siteghost or sitetraefik or sitemysql.
To view the logs of individual services, run:
docker service logs -f <service name>
Hurray!! Now your site must be up and running. Visit the URLs you provided above for Traefik and Ghost.
Subscribe to my Weekly Newsletter and Follow for more DevOps, SRE, Linux, Python, Golang, Artificial Intelligence, Data Science, and Self-Hosting.
Additional Resources and References:
- Setup SSH Keys:
- Add SSH Keys To DigitalOcean:
- Comparison between WordPress and Ghost:
- Traefik
- Docker