Installing Ghost CMS with Docker Compose on Ubuntu 18.04

Select distribution:
Traducciones al Español
Estamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.
Create a Linode account to try this guide with a $ credit.
This credit will be applied to any valid services used during your first  days.

Ghost is an open source blogging platform that helps you easily create a professional-looking online blog.

Ghost’s 1.0.0 version was the first major, stable release of the Ghost content management system (CMS). Ghost includes a Markdown editor, refreshed user interface, new default theme design, and more. Ghost has been frequently updated since this major release, and the current version at time of publication is 1.25.5.

In this guide you’ll deploy Ghost using Docker Compose on Ubuntu 18.04. Ghost is powered by JavaScript and Node.js. Using Docker to deploy Ghost will encapsulate all of Ghost’s Node dependencies and keep the deployment self-contained. The Docker Compose services are also fast to set up and easy to update.

Before you Begin

  1. If you have not already done so, create a Linode account and Compute Instance. See our Getting Started with Linode and Creating a Compute Instance guides.

  2. Follow our Setting Up and Securing a Compute Instance guide to update your system. You may also wish to set the timezone, configure your hostname, create a limited user account, and harden SSH access.

    Note
    Replace each instance of example.com in this guide with your Ghost site’s domain name.
  3. Complete the Add DNS Records steps to register a domain name that will point to your Ghost Linode.

  4. Your Ghost site will serve its content over HTTPS, so you will need to obtain an SSL/TLS certificate. Use Certbot to request and download a free certificate from Let’s Encrypt:

    sudo apt install software-properties-common
    sudo add-apt-repository ppa:certbot/certbot
    sudo apt update
    sudo apt install certbot
    sudo certbot certonly --standalone -d example.com
    

    These commands will download a certificate to /etc/letsencrypt/live/example.com/ on your Linode.

    When your certificate is periodically renewed, your web server needs to be reloaded in order to use the new certificate. This is usually accomplished by passing a web server reload command through Certbot’s --deploy-hook option.

    In your deployment, the web server will run in its own container, and the Certbot container would not be able to directly reload it. A workaround for this limitation would be needed to enable this architecture.

  5. Install Docker and Docker Compose before proceeding. If you haven’t used Docker before, review the Introduction to Docker, When and Why to Use Docker, and How to Use Docker Compose guides for some context on how these technologies work.

Install Docker

To install Docker CE (Community Edition), follow the instructions within one of the guides below:

To see installation instructions for other Linux distributions or operating systems like Mac or Windows, reference Docker’s official documentation here: Install Docker Engine

Install Docker Compose

Docker Compose is available in plugin and standalone variants. However, Docker’s official documentation prioritizes the plugin. Further, the plugin has a straightforward installation and works well with past Docker Compose commands.

These steps thus show how to install the Docker Compose plugin. If you are interested in installing the standalone Docker Compose application, follow Docker’s official installation guide.

Note

Many tutorials retain the Docker Compose standalone command format, which looks like the following:

docker-compose [command]

Be sure to replace this with the plugin’s command format when using this installation method. This typically just means replacing the hyphen with a space, as in:

docker compose [command]
  1. Enable the Docker repository for your system’s package manager. The repository is typically already enabled after you have installed the Docker engine. Follow our relevant guide on installing Docker to enable the repository on your system.

  2. Update your package manager, and install the Docker Compose plugin.

    • On Debian and Ubuntu systems, use the following commands:
    sudo apt update
    sudo apt install docker-compose-plugin
    • On CentOS, Fedora, and other RPM-based distributions, use the following commands:
    sudo yum update
    sudo yum install docker-compose-plugin

Install Ghost

The Ghost deployment has three components:

  • The Ghost service itself;
  • A database (MySQL) that will store your blog posts;
  • A web server (NGINX) that will proxy requests on HTTP and HTTPS to your Ghost service.

These services are listed in a single Docker Compose file.

Create the Docker Compose file

  1. Create and change to a directory to hold your new Docker Compose services:

    mkdir ghost && cd ghost
    
  2. Create a file named docker-compose.yml and open it in your text editor. Paste in the contents from the following snippet. Replace example.com with your domain, and insert a new database password where your_database_root_password appears. The values for database__connection__password and MYSQL_ROOT_PASSWORD should be the same:

    File: docker-compose.yml
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    version: '3'
    services:
    
      ghost:
        image: ghost:latest
        restart: always
        depends_on:
          - db
        environment:
          url: https://example.com
          database__client: mysql
          database__connection__host: db
          database__connection__user: root
          database__connection__password: your_database_root_password
          database__connection__database: ghost
        volumes:
          - /opt/ghost_content:/var/lib/ghost/content
    
      db:
        image: mysql:5.7
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: your_database_root_password
        volumes:
          - /opt/ghost_mysql:/var/lib/mysql
    
      nginx:
        build:
          context: ./nginx
          dockerfile: Dockerfile
        restart: always
        depends_on:
          - ghost
        ports:
          - "80:80"
          - "443:443"
        volumes:
           - /etc/letsencrypt/:/etc/letsencrypt/
           - /usr/share/nginx/html:/usr/share/nginx/html
  3. The Docker Compose file creates a few Docker bind mounts:

    • /var/lib/ghost/content and /var/lib/mysql inside your containers are mapped to /opt/ghost_content and /opt/ghost_mysql on the Linode. These locations store your Ghost content.

    • NGINX uses a bind mount for /etc/letsencrypt/ to access your Let’s Encrypt certificates.

    • NGINX also uses a bind mount for /usr/share/nginx/html so that it can access the Let’s Encrypt challenge files that are created when your certificate is renewed.

    Create directories for those bind mounts (except for /etc/letsencrypt/, which was already created when you first generated your certificate):

    sudo mkdir /opt/ghost_content
    sudo mkdir /opt/ghost_mysql
    sudo mkdir -p /usr/share/nginx/html
    

Create the NGINX Docker Image

The Docker Compose file relies on a customized NGINX image. This image will be packaged with the appropriate server block settings.

  1. Create a new nginx directory for this image in the ghost directory:

    mkdir nginx
    
  2. Create a file named Dockerfile in the nginx directory and paste in the following contents:

    File: nginx/Dockerfile
    1
    2
    3
    
    FROM nginx:latest
    
    COPY default.conf /etc/nginx/conf.d
  3. Create a file named default.conf in the nginx directory and paste in the following contents. Replace all instances of example.com with your domain:

    File: nginx/default.conf
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    server {
      listen 80;
      listen [::]:80;
      server_name example.com;
      # Useful for Let's Encrypt
      location /.well-known/acme-challenge/ { root /usr/share/nginx/html; allow all; }
      location / { return 301 https://$host$request_uri; }
    }
    
    server {
      listen 443 ssl http2;
      listen [::]:443 ssl http2;
      server_name example.com;
    
      ssl_protocols TLSv1.2;
      ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
      ssl_prefer_server_ciphers on;
      ssl_session_cache shared:SSL:10m;
    
      ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
      location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_pass http://ghost:2368;
      }
    }

    This configuration will redirect all requests on HTTP to HTTPS (except for Let’s Encrypt challenge requests), and all requests on HTTPS will be proxied to the Ghost service.

Run and Test Your Site

From the ghost directory start the Ghost CMS by running all services defined in the docker-compose.yml file:

docker-compose up -d

Verify that your blog appears by loading your domain in a web browser. It may take a few minutes for Docker to start your services, so try refreshing if the page does not appear when you first load it.

If your site doesn’t appear in your browser, review the logs generated by Docker for more information. To see these errors:

  1. Shut down your containers:

    cd ghost
    docker-compose down
    
  2. Run Docker Compose in an attached state so that you can view the logs generated by each container:

    docker-compose up
    
  3. To shut down your services and return the command prompt again, press CTRL-C.

Complete the Setup

To complete the setup process, navigate to the Ghost configuration page by appending /ghost to the end of your blog’s URL or IP. This example uses https://example.com/ghost.

  1. On the welcome screen, click Create your account:

    Ghost Welcome Screen

  2. Enter your email, create a user and password, and enter a blog title:

    Create Your Account Screen

  3. Invite additional members to your team. If you’d prefer to skip this step, click I’ll do this later, take me to my blog! at the bottom of the page:

    Invite Your Team Screen

  4. Navigate to the Ghost admin area to create your first post, change your site’s theme, or configure additional settings:

    Ghost Admin Area

Usage and Maintenance

Because the option restart: always was assigned to your services in your docker-compose.yml file, you do not need to manually start your containers if you reboot your Linode. This option tells Docker Compose to automatically start your services when the server boots.

Update Ghost

Your docker-compose.yml specifies the latest version of the Ghost image, so it’s easy to update your Ghost version:

docker-compose down
docker-compose pull && docker-compose up -d

Renew your Let’s Encrypt Certificate

  1. Open your Crontab in your editor:

    sudo crontab -e
    
  2. Add a line which will automatically invoke Certbot at 11PM every day. Replace example.com with your domain:

    0 23 * * *   certbot certonly -n --webroot -w /usr/share/nginx/html -d example.com --deploy-hook='docker exec ghost_nginx_1 nginx -s reload'
    

    Certbot will only renew your certificate if its expiration date is within 30 days. Running this every night ensures that if something goes wrong at first, the script will have a number of chances to try again before the expiration.

  3. You can test your new job with the --dry-run option:

    sudo bash -c "certbot certonly -n --webroot -w /usr/share/nginx/html -d example.com --deploy-hook='docker exec ghost_nginx_1 nginx -s reload'"
    

More Information

You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

This page was originally published on


Your Feedback Is Important

Let us know if this guide was helpful to you.


Join the conversation.
Read other comments or post your own below. Comments must be respectful, constructive, and relevant to the topic of the guide. Do not post external links or advertisements. Before posting, consider if your comment would be better addressed by contacting our Support team or asking on our Community Site.
The Disqus commenting system for Linode Docs requires the acceptance of Functional Cookies, which allow us to analyze site usage so we can measure and improve performance. To view and create comments for this article, please update your Cookie Preferences on this website and refresh this web page. Please note: You must have JavaScript enabled in your browser.