June 22, 2019

Ghost 2.0 blog on Dokku as NPM package + MYSQL + S3

Ghost 2.0 blog on Dokku as NPM package + MYSQL + S3

I recently updated my Ghost blog to version 2.0, which is running on a Dokku server. I couldn't see any instructions on how to do this with NPM packages, so here's how I managed to do it.

I did find some help in setting up the MYSQL database on this blog.

I'm going to call the blog myblog, but you should replace that with whatever you want to call yours.

Setup Ghost 2 and deploy to a Dokku server

In the first part, we create the NPM package blog, connected to a MYSQL database running on our Dokku server.

Set up the Dokku app

If you don't already have the dokku-mysql package installed, follow the installation instructions.

SSH into your Dokku server, then

dokku apps:create myblog

dokku mysql:create myblog
# mysql://<DB_USER>:<DB_PASSWORD>@<DB_HOST>:3306/<DB_NAME>

dokku mysql:link myblog myblog
dokku mysql:expose myblog
# -----> Service myblog exposed on port(s) [container->host]: 3306-><EXTERNAL_PORT>

dokku config:set --no-restart myblog NODE_ENV=production \
  database__connection__password=<DB_PASSWORD>

Take note of your DB_ configs & <EXTERNAL_PORT> for later.

Create your blog repo

This is how to create the blog repo from scratch. You will need to have Node.js (with NPM) installed.

mkdir myblog
cd myblog

# initalise the package.json
npm init
# add your own description
# entry point will be "node_modules/ghost/index.js"
# there's no "test" command
# use the defaults, or add your own preferences

Edit package.json to add the "start" script and "engines".

...
"scripts": {
  "start": "node node_modules/ghost/index.js",
  "test": "echo \"Error: no test specified\" && exit 1"
},
"engines": {
  "node": "10.16.0"
},
...

Install Ghost

npm i ghost
mkdir -p content/themes
cd content/themes
ln -s ../../node_modules/ghost/content/themes/casper casper
cd ../..

Create the development config

Create a new file called config.development.json in the root of your project, and add the following, replacing the DB info from earlier and your Dokku server domain.

{
  "url": "http://localhost:2368/",
  "server": {
    "port": 2368,
    "host": "127.0.0.1"
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "<DOKKU_SERVER_HOST>",
      "user": "mysql",
      "password": "<DB_PASSWORD>",
      "database": "<DB_NAME>",
      "port": "<EXTERNAL_PORT>"
    }
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": ["stdout"]
  },
  "process": "local",
  "paths": {
    "contentPath": "../../content/"
  }
}

Running locally

You should now be able to run your blog locally

npm start

Your database tables will be set up, and your blog will be running at http://localhost:2368/. You can check it out, and even set up your username and password at http://localhost:2368/ghost.

Deploying to Dokku server

Copy the config

cp config.development.json config.production.json

and edit it, removing the password, adding the Dokku server details, and using the DB_HOST from earlier.

{
  "url": "http://myblog.<DOKKU_DOMAIN>/",
  "server": {
    "port": 5000,
    "host": "0.0.0.0"
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "<DB_HOST>",
      "user": "mysql",
      "database": "<DB_NAME>",
      "port": "3306"
    }
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": ["stdout"]
  },
  "process": "local",
  "paths": {
    "contentPath": "../../content/"
  }
}

Setup the Git repo and deploy

(You will need to have Git installed).

git init
# we don't want to add the passwords
echo "config.development.json" >> .gitignore
git add .
git commit -m "Initial commit with Ghost."
git remote add dokku dokku@<DOKKU_DOMAIN>:myblog
git push dokku master

So now you have a blog, with the default, Casper theme, connected to a MYSQL.

You can connect to it at http://myblog.<DOKKU_DOMAIN>/ghost and start writing posts.

Add S3 Storage

If you upload any photos, and then deploy an update, all your images will be gone. To make them stick around, they need to be stored somewhere that's going to stick around – like Amazon Web Services S3.

Setup your AWS account, if you haven't already, and create a new S3 bucket. Let's call ours myblog. There are some regions which don't work out of the box, but eu-west-1 (Ireland) and us-east-1 (N. Virginia) work fine.

In the AWS console again, create an IAM user, eg. myblog-s3, and select Programatic Access. On the permissions page, select Attach existing policies directly and click to Create policy. For the policy click on the JSON editor and add the following policy. (Replace where it says myblog with the name of your S3 bucket.)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::myblog"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:PutObjectVersionAcl",
                "s3:DeleteObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::myblog/*"
        }
    ]
}

Save it with a name like myblog-s3-access, then go back to the IAM user. Refresh the list, and search and choose your new policy.

You should see your new Access Key and Secret Access Key

Copy the access key, SSH into your your Dokku server, and add it to your app's config.

dokku config:set myblog storage__s3__secretAccessKey=<SECRET_ACCESS_KEY>

Update Config

Now update your config.development.json

...
"storage": {
  "active": "s3",
  "s3": {
    "accessKeyId": "<ACCESS_KEY>",
    "secretAccessKey": "<SECRET_ACCESS_KEY>",
    "bucket": "myblog",
    "region": "<AWS_REGION>"
  }
}

And your config.production.json, without the secret.

...
"storage": {
  "active": "s3",
  "s3": {
    "accessKeyId": "<ACCESS_KEY>",
    "bucket": "myblog",
    "region": "<AWS_REGION>"
  }
}

Install an S3 storage adapter

npm i ghost-storage-adapter-s3
mkdir -p content/adapters/storage
cd content/adapters/storage
ln -s ../../../node_modules/ghost-storage-adapter-s3 s3
cd ../../..

Update Git, and push to redeploy

git add .
git commit -m "Added s3 storage."
git push dokku master

And now you can upload pretty pictures to your blog.

Increase your file size limit

By default, Nginx on Dokku only allows you to upload a max of 1MB. You can increase this by adding an Nginx config template to your repo. Copy the template file from the Dokku repo, and add it to your project root. Then edit it, adding a line just before the location in the server block. (I had two with HTTPS enabled).

# nginx.conf.sigil
...

  client_max_body_size 3m;

  location    / {
  
...

Update Git and push to deploy

git add .
git commit -m "Increased file upload limit."
git push dokku master

Add Gmail

If you want your server to be able to send emails, you can add Gmail settings. But don't use your regular password – set up an App Password.

Preparing Gmail config

  • From Gmail, click your icon in the top right and choose Google Account
  • Choose Security
  • Under Signing into Google, follow the steps to enable 2-Step Verification
  • Back in Security, follow the steps to create an App password
  • Copy this password to use as <GMAIL_APPLICATION_PASSWORD> below

Add config to Dokku

SSH into your Dokku server, then

dokku config:set --no-restart myblog mail__options__auth__pass="<GMAIL_APPLICATION_PASSWORD>"

Update Config

Now update your config.development.json

...
"mail": {
  "transport": "SMTP",
  "options": {
    "service": "Gmail",
    "auth": {
      "user": "<GMAIL_USER>",
      "pass": "<GMAIL_APPLICATION_PASSWORD>"
    }
  }
},

And your config.production.json, without the secret.

...
"mail": {
  "transport": "SMTP",
  "options": {
    "service": "Gmail",
    "auth": {
      "user": "<GMAIL_USER>",
    }
  }
},

Update Git and push to deploy

git add .
git commit -m "Added Gmail settings."
git push dokku master

Adding a theme

You can added a theme using NPM too. I'm using Massively here.

npm i github:TryGhost/Massively#32a8a6c # latest commit on GitHub

cd content/themes
ln -s ../../node_modules/massively massively
cd ../..

Update Git and push to deploy

git add .
git commit -m "Added Theme settings."
git push dokku master

Then log in to your blog at /ghost, go to Settings > Design and make the new theme active