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

# Complete Guide to Self-hosting Ghost CMS With Docker
- URL: https://linuxhandbook.com/deploy-ghost-docker/
- Published: 2021-06-25T15:43:40.000Z
- Updated: 2024-02-19T11:21:01.000Z
- Description: Learn how to self-host your Ghost blog with Docker on your own Linux server on the cloud!
- Author: Avimanyu Bandyopadhyay
- Tags: Tutorial

[Ghost](https://ghost.org/?via=abhishek53&ref=linuxhandbook.com) is an [open source content management system](https://itsfoss.com/open-source-cms/?ref=linuxhandbook.com) which is suitable for a blog, newsletter or membership website.

It is superfast and SEO optimized. We love it here at Linux Handbook. Our website uses Ghost, of course.

Now, you may [opt for a managed Ghost instance](https://ghost.org/pricing/?via=abhishek53&ref=linuxhandbook.com) from the makers of Ghost itself. It would cost you a lot but you won't have to put effort in deploying Ghost, updating it and maintaining it. And of course, it helps the development of Ghost project.

If you want to avoid spending a lot or take matters in your hand with a 'do it yourself' approach, you may self-host Ghost on your server.

In this tutorial, I'll show you the steps to deploy Ghost with Docker.

🚀

If you feel the deployment too complicated, use PikaPods and get a Ghost deployment from as less as $1.9 per month! [Start free with $5 welcome credit](https://www.pikapods.com/?ref=linuxhandbook..com) 😎

## Self-hosting Ghost with Docker

Here's the thing. Some cloud server providers like [DigitalOcean](https://m.do.co/c/d58840562553?ref=linuxhandbook.com) also provide one-click Ghost deployment. That could be the easy way out if you don't want the trouble of the initial Ghost set up and configuration.

![Ghost can be deployed easily on DigitalOcean servers](https://linuxhandbook.com/content/images/2021/06/ghost-one-click-deploy-digitalocean.png)

Ghost can be deployed easily on DigitalOcean servers

That aside, let's see what you need to deploy Ghost with Docker on a Linux server.

### Requirements

Aside from familiarity with Linux commands, [knowing the basics of Docker Compose](https://linuxhandbook.com/docker-compose-quick-start/) will also be helpful here. 

- A Linux server. You can use a physical server, virtual machine or cloud servers. You may [sign up with our partner Linode](https://linode.com/linuxhandbook?ref=linuxhandbook.com) and get $100 in free credits.
- Docker and Docker Compose installed on your server.
- Access to the DNS of your domain where you want to deploy Ghost.
- [Nginx reverse proxy setup](https://linuxhandbook.com/nginx-reverse-proxy-docker/) with www/non-www redirection and allowed upload limits.

[Free Linux Cloud Servers to Test or Host Your Web ApplicationsYou can try Linux cloud server platforms for free. Here’s how!![](https://linuxhandbook.com/assets/icon-192x192.png?v=428aff3e7b)Linux HandbookAbhishek Prakash![](https://linuxhandbook.com/content/images/2020/09/Free-linux-cloud-servers-1.png)](https://linuxhandbook.com/free-linux-cloud-servers/)

### Step 0: Get the initial set up ready

You need to have Docker and Docker Compose installed on your system. You may refer to these tutorials to get instructions 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/assets/icon-192x192.png?v=428aff3e7b)Linux HandbookAvimanyu Bandyopadhyay![](https://linuxhandbook.com/content/images/2020/06/Install_Docker_on_Ubuntu-1.png)](https://linuxhandbook.com/install-docker-ubuntu/)

[How to Install Docker Compose on Ubuntu \[Using Apt-Get\]Here are two ways to install Docker Compose on Ubuntu.![](https://linuxhandbook.com/assets/icon-192x192.png?v=428aff3e7b)Linux HandbookAbhishek Prakash![](https://linuxhandbook.com/content/images/2020/10/docker-compose-ubuntu.jpg)](https://linuxhandbook.com/docker-compose-ubuntu/)

Other than that, you also need to have Ngnix reverse proxy setup. This is beneficial if you want to have more than one Ghost or some other web service installed on the same server.

Now, I have covered this topic in detail in the tutorial linked below so I am not going to repeat the same steps here. However, you must have this setup on your system.

Follow this tutorial till Step 4:

[How to Use Nginx Reverse Proxy With Multiple Docker AppsLearn how you can deploy multiple web services on the same server using Nginx reverse proxy and docker containers.![](https://linuxhandbook.com/assets/icon-192x192.png?v=428aff3e7b)Linux HandbookDebdut Chakraborty![](https://linuxhandbook.com/content/images/2020/09/deploy-multiple-services-with-nginx-reverse-proxy-container-1.png)](https://linuxhandbook.com/nginx-reverse-proxy-docker/)

### Step 1: Preparing the deployment of Ghost

I use Jwilder reverse proxy method here because it takes into account SSL certificates, www/non-www redirection and allowed upload limits.

How to handle SSL certificates is already described in the link shared above in the requirements section. Additionally, I will describe how to enable www/non-www redirection and increase permitted upload limits.

#### WWW/non-WWW Redirection

Depending on your SEO preferences, you may want to set redirection of www to non-www or vice versa. For example, if your blog is hosted at domain.com, users visiting www.domain.com must be redirected to it (just how [GitHub's domain](https://www.github.com/?ref=linuxhandbook.com) works). 

Similarly, if you host it at www.domain.com, users visiting domain.com must be redirected (just how [Linode's domain](https://linode.com/?ref=linuxhandbook.com) works).

##### WWW to non-WWW

Create a file named `www.domain.com` within the nginx docker compose directory with the following content and save it:

```
rewrite ^/(.*)$ https://domain.com/$1 permanent;

```

##### Non-WWW to WWW

Create a file named `domain.com` within the nginx docker compose directory with the following content and save it:

```
rewrite ^/(.*)$ https://www.domain.com/$1 permanent;

```

Now, suppose you want to use WWW to non-WWW redirection. All you have to do is bind mount the file in the volumes section of your [Nginx service configuration](https://linuxhandbook.com/nginx-reverse-proxy-docker/#step-1-set-up-nginx-reverse-proxy-container):

```
      - ./www.domain.com:/etc/nginx/vhost.d/www.domain.com

```

#### Increase Permitted Upload Limits

Image uploads can be affected by the default maximum upload size of 50 MB. To set a maximum upload limit and avoid [issues when uploading images](https://ghost.org/docs/faq/image-upload-issues/?ref=linuxhandbook.com) on Docker, say for 1 GB, create a file named `client_max_upload_size.conf` and save it with the following content:

```
client_max_body_size 1G;

```

Later you need to mount it just as described with the previous file:

```
      - ./client_max_upload_size.conf:/etc/nginx/conf.d/client_max_upload_size.conf

```

Run `docker-compose up -d` from the Nginx directory to update your Nginx configuration.

First of all, the Ghost deployment configuration essentially consists of two main components:

- [MariaDB](https://mariadb.org/?ref=linuxhandbook.com) database backend
- [Ghost](https://ghost.org/?ref=linuxhandbook.com) itself as the web frontend

Since you're deploying Ghost with Docker, all the above components are set up as their own respective containers.

For the database service, I'll use an internal network called `ghost` as it only needs to be visible for the Ghost service.

```
networks:
  - ghost

```

But for the Ghost service, of course, the same `net` network used on the reverse proxy configuration must be specified along with the `ghost` network, and only then would it be possible to get it up and running with the Nginx Docker container.

```
networks:
  - net
  - ghost

```

Now consider how they are configured individually with Docker Compose:

For MariaDB, I use the [official MariaDB 10.5.3 image which is available on Docker Hub](https://hub.docker.com/%5F/mariadb?ref=linuxhandbook.com):

```
    ghostdb:
        image: mariadb:10.5.3
        volumes:
            - ghostdb:/var/lib/mysql
        restart: on-failure
        env_file:
            - ./mariadb.env
        networks:
            - ghost

```

Here I use a volume called `ghostdb` to store the database data at `/var/lib/mysql`. I also set the relevant environment variables in the `env_file` called `mariadb.env`:

```
MYSQL_RANDOM_ROOT_PASSWORD=1
MYSQL_USER=mariadbuser
MYSQL_PASSWORD=mariadbpassword
MYSQL_DATABASE=ghost

```

For the Ghost service itself, instead of using a `latest` tag, I specifically prefer to use the version number tagged on Docker Hub launched by the developers as a stable release. Here, at this time of writing, it is `4.5.0`:

```
    ghost:
        image: ghost:4.5.0
        volumes:
            - ghost:/var/lib/ghost/content
            - ./config.json:/var/lib/ghost/config.production.json
        env_file:
            - ./ghost-mariadb.env
        restart: on-failure
        depends_on: ghostdb
        networks:
            - net
            - ghost

```

The bind mounted `config.json` file consists of SMTP(Mailgun) and essential log rotation(error focused) settings:

```
{
  "url": "http://localhost:2368",
  "server": {
    "port": 2368,
    "host": "0.0.0.0"
  },
  "mail": {
    "transport": "SMTP",
    "options": {
        "service": "Mailgun",
        "host": "smtp.eu.mailgun.org",
        "port": 465,
        "secureConnection": true,
        "auth": {
            "user": "replace-me-with-a-mailgun-configured-email-address",
            "pass": "replace-me-with-the-relevant-mailgun-apikey-of-50-characters"
        }
    }
  },
  "logging": {
    "path": "content/logs/",
    "level": "error",
    "rotation": {
      "enabled": true,
      "count": 10,
      "period": "1d"
  },
    "transports": [
      "file",
      "stdout"
    ]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/lib/ghost/content"
  }
}

```

If it must use MySQL/MariaDB, the Ghost service has to rely on the `ghostdb` service for it to be operational. This can be made sure only when you have bind mounted the `ghost-mariadb.env` file(shown in the volumes section of the Ghost database service) with the correct database configuration according to `mariadb.env` in the database service, which would be: `database__client`, `database__connection__host`, `database__connection__user`, `database__connection__password` and `database__connection__database`.

I'll also assign each of the environment variables `VIRTUAL_HOST` and `LETSENCRYPT_HOST` to both `domain.com` as well as `www.domain.com` respectively, in order to ensure both exist. This absolutely ensures the redirections and SSL certificates work smoothly without issues. I want my main URL to be without www so I set it as `url=https://domain.com`. Since you will be using this setup for production-level usage, the `NODE_ENV` variable is set to production mode. These details also need to be added.

Therefore, the full environment file(`ghost-mariadb.env`) for the Ghost service would be:

```
VIRTUAL_HOST=domain.com,www.domain.com
LETSENCRYPT_HOST=domain.com,www.domain.com
url=https://domain.com
NODE_ENV=production

database__client=mysql
database__connection__host=ghostdb
database__connection__user=mariadbuser
database__connection__password=mariadbpassword
database__connection__database=ghost

```

Inconsistencies in the above data could make Ghost erroneously switch to SQLite. You don't want that. So please make sure all the above parameters correspond correctly with the database service discussed for `mariadb.env` above.

Each of the database services will have its own respective Docker volumes for storing user and content data. I'll create them as external volumes:

```
docker volume create ghostdb
docker volume create ghost

```

Now you need to include a `volumes` section within the docker compose file with the following details:

```
volumes:
  ghost:
    external: true
  ghostdb:
    external: true

```

You now have the necessary components for deploying Ghost.

### Step 2: Deploying Ghost

Now you should have the docker-compose file ready. It's time to use this file.

Create the Ghost docker compose directory on your server:

```
mkdir ghost

```

Go into the directory to edit the necessary files:

```
cd ghost

```

Now create the following docker-compose file based on our discussions so far:

```
version: '3.7'
services:
    ghostdb:
       image: mariadb:10.5.3
       volumes:
          - ghostdb:/var/lib/mysql
       restart: on-failure
       env_file:
          - ./mariadb.env
       networks:
          - ghost

    ghost:
      image: ghost:4.5.0
      volumes:
        - ghost:/var/lib/ghost/content
        - ./config.json:/var/lib/ghost/config.production.json
      env_file:
        - ./ghost-mariadb.env
      restart: on-failure
      depends_on: 
        - ghostdb
      networks:
        - net
        - ghost
volumes:
  ghost:
    external: true
  ghostdb:
    external: true

networks:
  net:
    external: true
  ghost:
    internal: true

```

Also, do not forget to create the other configuration files as discussed above: `config.json`, `mariadb.env` and `ghost-mariadb.env` within the same directory.

Start the Ghost instance:

```
docker-compose up -d

```

Access the Ghost domain specified in the configuration using your specified domain URL.

### Step 3: Setting up your Ghost Admin account

Note that in order to setup your administrator account, you must go to ***domain.com/ghost*** and follow the on-screen instructions until you have claimed your site as ghost admin.

To double-check, do confirm you are indeed using MySQL/MariaDB and not SQLite. Navigate to your user icon at the bottom left:

![](https://linuxhandbook.com/content/images/2021/06/ghost-navigate-1-1.png)

![](https://linuxhandbook.com/content/images/2021/06/ghost-navigate-2-2.png)

Now you can be sure that you are actually using the separate database container which will display as `mysql` regardless of whether you are using a MySQL or MariaDB Docker image:

![](https://linuxhandbook.com/content/images/2021/05/ghost-mariadb-mysql.png)

Ghost displays mysql even if you use MariaDB

You can also see that the other three parameters: Version, Environment and Mail, are set as expected based on our above-mentioned steps. So that's it! You have successfully deployed Ghost as a self-hosted instance on your server!

## Tips for maintaining your self-hosted Ghost instance

Here are a [few tips that will help you in maintaining your Ghost instance](https://linuxhandbook.com/ghost-hosting-tips/).

### Monitor Ghost Logs in Real-time

If you want to check the container's logs while it's deployed in real time, you can run:

```
docker logs -f ghost_ghost_1

```

### Backup and Restore Ghost Volumes without Downtime

Using a cloud + local approach, you can backup and restore your ghost volumes without downtime.

[Definitive Guide on Backup and Restore of Docker ContainersHarness both the cloud and your local system to backup and restore your Docker containers.![](https://linuxhandbook.com/assets/icon-192x192.png?v=60bbc3c741)Linux HandbookAvimanyu Bandyopadhyay![](https://linuxhandbook.com/content/images/2021/05/backup-restore-docker-services.png)](https://linuxhandbook.com/backup-restore-docker-services/)

### Update Ghost Containers without Downtime

With the `--scale` flag on Docker Compose, you can create a new container based on the latest version of Ghost. When it's done, you can remove the old one. This results in zero downtime.

[Updating Docker Containers With Zero DowntimeA step by step methodology that can be very helpful in your day to day DevOps activities without sacrificing invaluable uptime.![](https://linuxhandbook.com/assets/icon-192x192.png?v=60bbc3c741)Linux HandbookAvimanyu Bandyopadhyay![](https://linuxhandbook.com/content/images/2020/12/Updating-containers-without-downtime.png)](https://linuxhandbook.com/update-docker-container-zero-downtime/)

There are a few more tips you can read in the article below.

[7 Useful Tips for Self-hosting a Ghost Blog With DockerA useful checklist that can prevent many recurring maintenance issues from happening on your Ghost Docker instance after you deploy it.![](https://linuxhandbook.com/assets/icon-192x192.png?v=428aff3e7b)Linux HandbookAvimanyu Bandyopadhyay![](https://linuxhandbook.com/content/images/2021/03/ghost-self-hosting-tips.png)](https://linuxhandbook.com/ghost-hosting-tips/)

You may also want to [Deploy & Manage Ghost Themes Using GitHub Actions](https://linuxhandbook.com/ghost-theme-github-actions/) to simplify your work.

If you encounter a bug, have problems or have a suggestion, please let me know by leaving a comment below.