> ## Content Index
> Fetch the complete content index at: https://linuxhandbook.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Self-host a Ghost Blog With Traefik
- URL: https://linuxhandbook.com/self-host-ghost-traefik/
- Published: 2022-06-27T11:55:00.000Z
- Updated: 2024-02-19T11:17:16.000Z
- Description: Learn to easily start a blog using Ghost and secure it using Traefik on a Docker container.
- Author: LHB Community
- Tags: Tutorial, DevOps

[Ghost](https://ghost.org/?via=abhishek53&ref=linuxhandbook.com) is a very popular open-source content management system. Started as an alternative to WordPress and it went on to become an alternative to Substack by focusing on membership and newsletter. 

The creators of [Ghost offer managed Pro hosting](https://ghost.org/pricing/?via=abhishek53&ref=linuxhandbook.com) but it may not fit everyone's budget.

Alternatively, you can self-host it on your own [cloud servers](https://linuxhandbook.com/free-linux-cloud-servers/). On Linux handbook, we already have a [guide on deploying Ghost with Docker in a reverse proxy setup](https://linuxhandbook.com/deploy-ghost-docker/). 

🚀

Another alternative is to deploy Ghost effortlessly in the cloud with PikaPods from as less as $1.9 per month! [Start free with $5 welcome credit](https://www.pikapods.com/?ref=linuxhandbook..com) 😎

Instead of [Ngnix reverse proxy](https://linuxhandbook.com/nginx-reverse-proxy-docker/), you can also use another software called [Traefik](https://traefik.io/?ref=linuxhandbook.com) with Docker. It is a popular open-source cloud-native application proxy, API Gateway, Edge-router, and more.

I use Traefik to secure my websites using an SSL certificate obtained from [Let's Encrypt](https://letsencrypt.org/?ref=linuxhandbook.com). Once deployed, Traefik can automatically manage your certificates and their renewals.

In this tutorial, I'll share the necessary steps for deploying a Ghost blog with Docker and Traefik.

Here's what you need for this tutorial:

- A domain and access to its DNS settings
- A cloud server like the ones from [DigitalOcean](https://m.do.co/c/d58840562553?ref=linuxhandbook.com) or [Linode](https://www.linode.com/?r=19db9d1ce8c1c91023c7afef87a28ce8c8c067bd&ref=linuxhandbook.com)
- Decent knowledge of Linux command line
- Decent knowledge of Docker

With that aside, let's see how to go about it.

## Step 1\. Get a domain (if you don't have one)

I always recommend deploying tools on real domains even if it is for test purposes. 

Domains are not expensive these days. You can find some good, [inexpensive domains at NameCheap](https://namecheap.pxf.io/7mkbW5?ref=linuxhandbook.com).

For test purposes, you can get any domain that is available at the cheapest price. Usually, domains with obscure TLDs like .club are very cheap. Instead of renewing the next year, you can buy another one at a cheap price.

If it is for a real, public-facing website, go for a domain that is suitable for your branding. I always prefer .com domains over any other TLD.

[Buy a domain name - Register cheap domain names from $0.99 - NamecheapRegister domain names at Namecheap. Buy cheap domain names and enjoy 24/7 support. With over 13 million domains under management, you know you’re in good hands.![](https://www.namecheap.com/assets/img/nc-icon/namecheap-icon-152x152.png)65% off](https://namecheap.pxf.io/7mkbW5?ref=linuxhandbook.com)

For the production website, I recommend using Cloudflare for a faster website. You can use it for free. Here's [how to set up your DNS with Cloudflare](https://www.namecheap.com/support/knowledgebase/article.aspx/9607/2210/how-to-set-up-dns-records-for-your-domain-in-cloudflare-account/?ref=linuxhandbook.com).

## 2\. Setting up your Cloud Instance

I use DigitalOcean to host my website - [Narasimman Tech](https://narasimmantech.com/?ref=linuxhandbook.com). It is easy and cheap to set up. If you are a new user, [DigitalOcean gives you a $100 credit](https://m.do.co/c/d58840562553?ref=linuxhandbook.com), valid for 60 days of server usage.

[DigitalOcean – The developer cloudHelping millions of developers easily build, test, manage, and scale applications of any size – faster than ever before.![](https://m.do.co/_next/static/media/android-chrome-512x512.5f2e6221.png)Explore our products![](https://www.digitalocean.com/_next/static/media/social-share-default.e8530e9e.jpeg)](https://m.do.co/c/d58840562553?ref=linuxhandbook.com)

If you are in doubt, refer to our list of free cloud servers. You can get some free credits for trying out a new service.

I let you create a new server on your preferred cloud service. 

## 3\. Setting up Docker and Docker Swarm

I believe that you can figure out how to use SSH to connect to the remote servers. 

```bash
ssh root@<IP address of the Droplet>

```

Once you are logged in to your server, you need to get Docker configured. Since the installation of Docker is different for different distributions, I am not going to cover it. If you need help, here's a tutorial for Ubuntu.

[How to Install Docker on Ubuntu Linux \[Beginner Tutorial\]In the first of Docker tutorial series, you’ll learn to install the latest version of Docker Engine Community Edition on Ubuntu Linux.![](https://linuxhandbook.com/content/images/size/w256h256/2021/08/Linux-Handbook-New-Logo.png)Linux HandbookAvimanyu Bandyopadhyay![](https://linuxhandbook.com/content/images/2020/06/Install_Docker_on_Ubuntu-1.png)](https://linuxhandbook.com/install-docker-ubuntu/)

We will be running our services in a [Docker Swarm](https://docs.docker.com/engine/swarm/?ref=linuxhandbook.com) Environment. To start a Docker Swarm Environment run,

```bash
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.

## 4\. 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.

```bash
mkdir website

cd website

```

You have to create a couple of files and directories to store Traefik configuration files and your SSL keys:

Create a new directory called **‘data’** and change the directory into it.

```bash
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.

```bash
touch traefik.yml acme.json

chmod 600 acme.json

```

Open the file using any editor. 

```bash
nano traefik.yml

```

Paste the following code in the **traefik.yml** file.

```bash
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"

```

**You have to change a few variables:**

Change yourdomain.com to the domain you own.

```yaml
          - 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.

```bash
mkdir configurations

cd configurations

touch dynamic.yml

nano dynamic.yml

```

```yaml
# 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’**

[Deployment from ScratchAn introductory book to web application deployment![](https://gumroad.com/img/android-icon-192x192.png)A complete guide to web application deployment![](https://deploymentformakers.com/vpsformakerscatrepeats.gif)](https://gumroad.com/a/298436531?ref=linuxhandbook.com)

## 5\. Setting up Traefik and Ghost

Now go back to our main directory, i.e., the **‘website’** directory in my case, which you created at first.

```bash
cd ~/website

```

Now create a file called **docker-compose.yml** for editing.

```bash
nano docker-compose.yml 

```

Paste the following:

```yaml
# 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

```

Remember to set the environment variables. If you don’t know how to get your Cloudflare account’s API Email and DNS API Token, read this article from [Cloudflare](https://developers.cloudflare.com/api/tokens/create/?ref=linuxhandbook.com).

```yaml
    environment:
	  - CF_API_EMAIL=
	  - CF_DNS_API_TOKEN=

```

Don’t close the file yet. **You have to change a few parameters here**.

Replace **‘traefik.yourdomain.com;** with a subdomain.

```yaml
- "traefik.http.routers.traefik-secure.rule=Host(`traefik.yourdomain.com`)"

```

Replace **‘secretpassword’** with a new password.

```yaml
database__connection__password: secretpassword

```

Replace **‘**[**https://yourdomain.com**](https://yourdomain.com/?ref=linuxhandbook.com)**'** with your URL.

```yaml
url: https://yourdomain.com

```

Replace **‘yourdomain.com’** with a subdomain, or use the full domain.

```yaml
- "traefik.http.routers.ghost-secure.rule=Host(`yourdomain.com`)"

```

Finally, replace **‘secretpassword’** with the password you set above on point two.

```yaml
MYSQL_ROOT_PASSWORD: secretpassword

```

## 6\. 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:

![Directory structure for Ghost setup with Traefik](https://linuxhandbook.com/content/images/2022/06/traefik-ghost-setup-directory.png)

Now from this directory, run the command. You can change ‘site’ to anything you want.

```bash
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:

```bash
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:

```bash
docker service ps <service name>

```

Replace <service name> with site, site\_ghost or site\_traefik or site\_mysql.

To view the logs of individual services, run:

```bash
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. Follow me on** [**Twitter**](https://twitter.com/narasimman%5Ftech?ref=linuxhandbook.com) **\- @narasimman\_tech**