This is the story of how I got this blog up and running, and hopefully it's helpful to other people looking to self-host Ghost (hey that rhymed).
After some trouble getting the official Docker image for Ghost to work outside of my development box, I decided to make my own image.
Checking the Ghost Github repository, the documentation says:
- Download the latest release of Ghost
- Unzip in the location you want to install
- Fire up a terminal
npm install --production
- Start Ghost!
- Local environment:
npm start
- On a server:
npm start --production
http://localhost:2368/ghost
Converted into a Dockerfile, those instruction look something like the following.
Dockerfile
FROM download13/node
WORKDIR /ghost
COPY config.js /ghost/config.js
RUN wget -O ghost.zip https://github.com/TryGhost/Ghost/releases/download/0.7.9/Ghost-0.7.9.zip \
&& unzip ghost.zip \
&& rm ghost.zip \
&& npm install --production \
&& npm cache clean \
&& rm -rf /tmp/npm*
CMD ["npm", "start", "--production"]
The npm cache clean
and rm -rf /tmp/npm*
bits are just cleanup to keep the image size down. The important part here is the COPY config.js /ghost/config.js
line, which will bring in our custom config file.
Speaking of which...
config.js
module.exports = {
production: {
url: process.env.BLOG_URL,
mail: {},
database: {
client: 'sqlite3',
connection: {
filename: '/ghost/content/data/ghost.db'
},
debug: false
},
server: {
host: '0.0.0.0',
port: process.env.PORT || '80'
}
}
};
Two fields are set via environment variables. The url
field (set by BLOG_URL
) will be the base URL for your blog. It's required if you don't want a somewhat broken experience. server.port
defaults to 80 which will be fine for most uses, but can be overridden by setting the PORT
environment variable.
Let's test what we've got so far.
# docker build -t ghosttest .
...
Successfully built 767d2c6e0177
Sweet! This might actually work. Now let's try running it.
Note that 192.168.99.100
is the IP of the Docker host VM on my computer. Replace it with localhost
or the address of your Docker host if different.
# docker run -it -e "BLOG_URL=http://192.168.99.100" -p 80:80 ghosttest
> ghost@0.7.9 start /ghost
> node index
WARNING: Ghost is attempting to use a direct method to send email.
It is recommended that you explicitly configure an email service.
Help and documentation can be found at http://support.ghost.org/mail.
Migrations: Database initialisation required for version 004
Migrations: Creating tables...
Migrations: Creating table: posts
Migrations: Creating table: users
Migrations: Creating table: roles
Migrations: Creating table: roles_users
Migrations: Creating table: permissions
Migrations: Creating table: permissions_users
Migrations: Creating table: permissions_roles
Migrations: Creating table: permissions_apps
Migrations: Creating table: settings
Migrations: Creating table: tags
Migrations: Creating table: posts_tags
Migrations: Creating table: apps
Migrations: Creating table: app_settings
Migrations: Creating table: app_fields
Migrations: Creating table: clients
Migrations: Creating table: client_trusted_domains
Migrations: Creating table: accesstokens
Migrations: Creating table: refreshtokens
Migrations: Running fixture populations
Migrations: Creating owner
Migrations: Ensuring default settings
Ghost is running in production...
Your blog is now available on http://192.168.99.100
Ctrl+C to shut down
Navigating to http://192.168.99.100
I see the following page.
Woohoo! Done! Well, not quite.
There's a little more to deploying it, but first I'm going to put what we have so far into an Docker Hub Automated Build for the sake of convenience. Now whenever we want to use this image, we can just refer to it as download13/ghost
.
If you want to deploy this on your personal server, you're probably going to want to keep the contents of your blog even if you have to re-create it's container.
There are two directories that need to persist across containers if you want your blog to work correctly.
/ghost/content/data
contains the sqlite database file which stores all your users/posts/tags/etc.
/ghost/content/images
is where all uploaded images are kept.
Let's add these volumes to our container.
# mkdir -p ~/ghost_data/{data,images}
# docker run -it -e "BLOG_URL=http://192.168.99.100" -p 80:80 -v ~/ghost_data/data:/ghost/content/data -v ~/ghost_data/images:/ghost/content/images download13/ghost
Of course running it from Docker Compose is a bit easier.
docker-compose.yml
version: "2"
services:
blog:
image: download13/ghost
restart: always
volumes:
- ~/ghost_data/data:/ghost/content/data
- ~/ghost_data/images:/ghost/content/images
environment:
BLOG_URL: http://192.168.99.100
# mkdir -p ~/ghost_data/{data,images}
# docker-compose up -d
Creating ghosttest_blog_1
And that's it! Now you've got a working Ghost blog!