Using Docker Compose & Mysql to set up a ghost blog
This is a post that I wanted to write long time ago, but I finally had time to. At this point, if you look closely, it should come as no surprise that my blog is powered by Ghost, it's also deployed inside a Docker container.
Ghost is a widely used blogging platform written entirely in javascript, both on the client and on the server using node and expressjs. By default, it uses a SQLite instance for persistence, but I've never really liked SQLite that much, and also to fit an even better use-case for using Docker, while maintaining separation of concerns. So we're splitting up the containers by responsibility and using MySQL as our "favorite database"!
We will split the responsibilities up in Docker containers which runs completely isolated from each other. One for the ghost platform and one for the database.
Using Docker Compose, we can easily manage our Docker containers and their respective builds. If you've never played around with Docker before, I would encourage you to go familiarize your self with it's documentation before jumping in to Docker Compose.
Docker-comp provides a clean interface for managing containers, and lets you handle all your app's services from a single source.
1- Installing Docker & Docker-Compose
Docker is well documented, so all what you've to do is to follow instructions for installing Docker engine and Docker Compose ;)
2- Docker Compose Config: docker-compose.yml
version: '2'
ghost:
image: ghost:latest
container_name: ghost-blog
environment:
- NODE_ENV=production
- MYSQL_DATABASE={{db-name}} # Change {{db-name}}
- MYSQL_USER={{username}} # Change {{username}}
- "MYSQL_PASSWORD={{db-password}}" # Change {{db-password}}
- "MAILGUN_USER={{mailgun-user}}" # Change {{mailgun-user}}
- "MAILGUN_PASSWORD={{mailgun-password}}" # Change {{mailgun-password}}
volumes:
- ./ghost:/var/lib/ghost
ports:
- 2368:2368
depends_on:
- mysql
restart: always
mysql:
image: mysql:latest
container_name: ghost-db
environment:
- MYSQL_DATABASE={{db-name}} # Change {{db-name}}
- MYSQL_ROOT_PASSWORD={{root-password}} # Change {{root-password}}
- MYSQL_USER={{username}} # Change {{username}}
- MYSQL_PASSWORD={{db-password}} # Change {{db-password}}
volumes:
- ./db:/var/lib/mysql
restart: always
Our Compose file takes the following form. Two services are defined, a Mysql service and a Ghost service. The Mysql service is configured via environment variables set in the docker-compose file. We use the official Mysql Docker image that Compose will automatically pull from the Docker hub. The Ghost service using the official Ghost image; it depends on the Mysql service to ensure that the database will start first. We expose the default port of Ghost 2368
to port 2368 of our Docker host. we also use data Volumes for both services.
Ghost Config: config.js
// # Ghost Configuration
// Setup your Ghost install for various [environments](http://support.ghost.org/config/#about-environments).
// Ghost runs in `development` mode by default. Full documentation can be found at http://support.ghost.org/config/
var path = require('path'),
config;
config = {
// ### Production
// When running Ghost in the wild, use the production environment.
// Configure your URL and mail settings here
production: {
url: 'https://your-url-here.com',
mail: {},
database: {
atabase: {
client: 'mysql',
connection: {
host : 'mysql',
user : process.env.MYSQL_USER, // Copy the MYSQL_USER from docker-compose.yml
password : process.env.MYSQL_PASSWORD, // Copy the MYSQL_PASSWORD from docker-compose.yml
database : process.env.MYSQL_DATABASE, // Copy the MYSQL_DATABASE from docker-compose.yml
charset : 'utf8'
}
},
server: {
host: '0.0.0.0',
port: '2368'
},
mail: {
transport: 'SMTP',
options: {
service: 'Mailgun',
auth: {
user: process.env.MYSQL_USER,
pass: process.env.MYSQL_USER
}
}
},
paths: {
contentPath: path.join(process.env.GHOST_CONTENT, '/')
},
},
// ### Development **(default)**
development: {
// The url to use when providing links to the site, E.g. in RSS and email.
// Change this to your Ghost blog's published URL.
url: 'http://localhost:2368',
// Example refferer policy
// Visit https://www.w3.org/TR/referrer-policy/ for instructions
// default 'origin-when-cross-origin',
// referrerPolicy: 'origin-when-cross-origin',
// Example mail config
// Visit http://support.ghost.org/mail for instructions
// ```
// mail: {
// transport: 'SMTP',
// options: {
// service: 'Mailgun',
// auth: {
// user: '', // mailgun username
// pass: '' // mailgun password
// }
// }
// },
// ```
// #### Database
// Ghost supports sqlite3 (default), MySQL & PostgreSQL
database: {
client: 'sqlite3',
connection: {
filename: path.join(process.env.GHOST_CONTENT, '/data/ghost-dev.db')
},
debug: false
},
// #### Server
// Can be host & port (default), or socket
server: {
// Host to be passed to node's `net.Server#listen()`
host: '0.0.0.0',
// Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
port: '2368'
},
// #### Paths
// Specify where your content directory lives
paths: {
contentPath: path.join(process.env.GHOST_CONTENT, '/')
}
},
// **Developers only need to edit below here**
// ### Testing
// Used when developing Ghost to run tests and check the health of Ghost
// Uses a different port number
testing: {
url: 'http://0.0.0.0:2369',
database: {
client: 'sqlite3',
connection: {
filename: path.join(process.env.GHOST_CONTENT, '/data/ghost-test.db')
},
pool: {
afterCreate: function (conn, done) {
conn.run('PRAGMA synchronous=OFF;' +
'PRAGMA journal_mode=MEMORY;' +
'PRAGMA locking_mode=EXCLUSIVE;' +
'BEGIN EXCLUSIVE; COMMIT;', done);
}
},
useNullAsDefault: true
},
server: {
host: '0.0.0.0',
port: '2369'
},
logging: false
},
// ### Testing MySQL
// Used by Travis - Automated testing run through GitHub
'testing-mysql': {
url: 'http://0.0.0.0:2369',
database: {
client: 'mysql',
connection: {
host : '0.0.0.0',
user : 'root',
password : '',
database : 'ghost_testing',
charset : 'utf8'
}
},
server: {
host: '0.0.0.0',
port: '2369'
},
logging: false
},
// ### Testing pg
// Used by Travis - Automated testing run through GitHub
'testing-pg': {
url: 'http://0.0.0.0:2369',
database: {
client: 'pg',
connection: {
host : '0.0.0.0',
user : 'postgres',
password : '',
database : 'ghost_testing',
charset : 'utf8'
}
},
server: {
host: '0.0.0.0',
port: '2369'
},
logging: false
}
};
module.exports = config;
It's a long configuration, I acknowledge, but now everything could be dynamically configured.
We already specified on Docker compose file some env variables: database engine to use and it's parameters, as well as the Node environment to run under (you'd be surprised on how many people run Ghost on development :P).
And voila! Docker Compose is a very handy tool that helps you write a distributed application definition in a single YAML file.