Photo by Carlos Nunez / Unsplash

How To Install Ghost For A Production | Docker Swarm

Docker Oct 6, 2021

You'll need Linux server, Public IP, domain name, ssl and docker installed running in swarm.

Check docker installation here if you're using ubuntu 20.04:

How To Install Docker and Docker-compose | Ubuntu 20.04
There are other ways to do it (please avoid using snap), however this is a proper one. Steps: sudo apt updatesudo apt install -y apt-transport-https ca-certificates curl software-properties-common wgetsudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -sudo add-apt-repo…

We'll be using docker (swarm) for mysql-8, nginx and ghost.

I like to have separate stacks when working with docker swarm.

Mysql Step:

Create a folder for your mysql and cd into it. Then create two more folders.

mkdir conf.d dbdata

Create a file inside conf.d called custom.cnf.

nano conf.d/custom.cnf

Add:

[mysqld]
sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION"
default-authentication-plugin=mysql_native_password
character-set-server=utf8
collation-server=utf8_general_ci

Create docker-compose.yml and edit.

version: "3.7"

services:

  prod:
    image: mysql8
    hostname: mysql-8
    command: '--default-authentication-plugin=mysql_native_password'
    networks:
      - durbok-net
    deploy:
      placement:
        constraints:
        - node.role == manager
      replicas: 1
      restart_policy:
        condition: on-failure
    volumes:
      - ./conf.d:/etc/mysql/conf.d
      - ./dbdata:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: supersecretpasshere


networks:
  durbok-net:
    external: true

Create external docker network (you can use different name) but append it also in docker-compose.yml file.

docker network create -d overlay durbok-net

Deploy Mysql8.

docker stack deploy -c docker-compose.yml mysql8

Create database and database user for ghost.

Enter mysql container with docker exec -it and bash in the end.

docker exec -it mysql8_prod.xxxxxxxxxxxxxx bash

Next run and type in your previously defined password for mysql (in docker-compose.yml).

mysql -u root -p

You should get:
mysql>

Creating database and user check:

Mysql Basics
Mysql

Don't forget to give user  rights over db and flush privileges. Also this db container can be used for other projects like other ghost or wordpress sites.

Lets setup ghost.

Create a folder for ghost and cd into it. Create a docker-compose.yml.

version: "3.7"

services:

  prod:
    image: ghost:alpine
    volumes:
      - ./content/images:/var/lib/ghost/content/images
      - ./content/themes:/var/lib/ghost/content/themes
      - ./content/apps:/var/lib/ghost/content/apps
      - ./content/data:/var/lib/ghost/content/data
    environment:
      - url=https://blog.kafana.dev  # add your domain here
      - database__client=mysql
      - database__connection__host=mysql8_prod # db host (name of the docker service)
      - database__connection__user=db_user
      - database__connection__password=db_pass
      - database__connection__database=db_name
      - database__pool__min=0
      - VIRTUAL_PORT=2368
    networks:
      - durbok-net
    deploy:
      placement:
        constraints:
          - node.role == manager
      replicas: 1
      restart_policy:
        condition: on-failure

networks:
  durbok-net:
    external: true

Create following folders for data persistence.

mkdir -p content/{images,themes,apps,data}

Deploy:

docker stack deploy -c docker-compose.yml ur_name

Lets setup nginx:

Same as before. Create a separate folder and cd into it.

mkdir nginx-conf

Create docker-compose.yml.

version: '3.7'

services:
  prod:
    image: nginx:stable-alpine
    volumes:
      - ./nginx-conf:/etc/nginx/conf.d
      - /path/to/ssl:/etc/nginx/ssl
      - /path/to/static/webfolder:/var/www/html/webfolder   # optional
    networks:
      - durbok-net
    deploy:
      placement:
        constraints:
          - node.role == manager
      replicas: 1
      restart_policy:
        condition: on-failure
    ports:
      - 80:80
      - 443:443

networks:
  durbok-net:
    external: true

volumes:
  nginx-conf:

For creating ssl I'll create separate post and link it here.

Before we deploy nginx create ur_name.conf file inside nginx-conf folder.
Something like this:

server {
  listen 80;
  listen [::]:80;
  server_name blog.kafana.dev;  # add you domain here
  rewrite ^ https://$http_host$request_uri? permanent;
}

server {

  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name blog.kafana.dev;  # add you domain here

  error_log  /var/log/nginx/blog.kafana.dev_error.log;
  access_log /var/log/nginx/blog.kafana.dev_access.log;


  location ^~  {

    proxy_set_header        Host $host:$server_port;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;

    client_max_body_size 256M;

    # Fix the "It appears that your reverse proxy set up is broken" error.
    proxy_pass              http://blog_prod:2368;
    proxy_read_timeout      90;

    # Required for new HTTP-based CLI
    proxy_http_version 1.1;
    proxy_request_buffering off;
  }


location ~ /\.(?!well-known).* {
    deny all;
    access_log off;
    log_not_found off;
}

  add_header Content-Security-Policy upgrade-insecure-requests;

  ssl_certificate /etc/nginx/ssl/ur_ssl_here.pem;
  ssl_certificate_key /etc/nginx/ssl/_ur_ssl_here.key;
#  ssl_dhparam /etc/nginx/ssl/dhparams.pem;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:5m;


  #SSL Security
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
  #XP and IE6 support
  #ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
  ssl_ecdh_curve secp384r1;
  ssl_prefer_server_ciphers on;
  ssl_session_tickets off;

  proxy_set_header X-Forwarded-For $remote_addr;

  #Compress and optimize delivery of files


  gzip on;
  gzip_comp_level    5;
  gzip_min_length    256;
  gzip_vary          on;
  gzip_types
    application/atom+xml
    application/javascript
    application/json
    application/ld+json
    application/manifest+json
    application/rss+xml
    application/vnd.geo+json
    application/vnd.ms-fontobject
    application/x-font-ttf
    application/x-web-app-manifest+json
    application/xhtml+xml
    application/xml
    font/opentype
    image/bmp
    image/svg+xml
    image/x-icon
    text/cache-manifest
    text/css
    text/plain
    text/vcard
    text/vnd.rim.location.xloc
    text/vtt
    text/x-component
    text/x-cross-domain-policy;
    # text/html is always compressed by gzip module

}

Just one more command.

docker stack deploy -c docker-compose.yml ur_name_here

All done.

You can also use this nginx for other project just reload when adding new *.conf files with command:

docker exec -it nginx_xxxxxxxxxxxxxxxxxxxxxx nginx -s reload

Tags