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.
- 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: firstname.lastname@example.org storage: acme.json keyType: EC384 dnsChallenge: provider: cloudflare resolvers: - "188.8.131.52:53" - "184.108.40.206: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
- 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.
- 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.
- Replace ‘secretpassword’ with a new password.
- Replace ‘https://yourdomain.com' with your URL.
- Replace ‘yourdomain.com’ with a subdomain, or use the full domain.
- Finally, replace ‘secretpassword’ with the password you set above on point two.
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: