Migrating to Ghost 1.x with Docker
In this post I will share my experiences with upgrading our d&b blog to Ghost 1.8.7 in one of our testing environments - based on Ghost's latest official Docker image:
- Upgrade Scenario
- Steps
- Key Takeaways
- Sample Files
- Conclusion
- Further Information
1. Upgrade Scenario
- This blog always was (before and after) based on the official Docker image for Ghost:
- So the upgrade is from 0.11.11 to 1.8.7
- For the lack of better knowledge when starting to use Ghost, our first steps started (unknowingly) in
development
mode:- So I had (as probably many others) the confusion when switching to
production
with missing/switching paths (/var/lib/ghost/
vs./usrc/src/ghost
). - Thus the container used to have two
docker volumes
.
- So I had (as probably many others) the confusion when switching to
- Our blog used the
USER user
instruction in its Dockerfile. - Keep using SQLite as database also for version 1.x which still is the default for the docker-based setup and does not require additional steps.
2. Steps
I used the official Migration guide. It is well explained and also mostly applicable when setting it up in a container-based environment.
The most important things to note before starting it I will summarize in the next chapter. After that are basic sample files to illustrate the chosen configs.
3. Key Takeaways:
Here are the most important things to keep in mind:
Export/Import ... No backup restore
Besides your image directory you cannot use restored backups of the docker volumes
... you need to export your blog in the Ghost web UI to a JSON file and import it to the new container.
Default Node_ENV
now is production
... not development
In contrast to the previous versions 0.x which started in development
mode (NODE_ENV
), the new versions of Ghost 1.0.0 and above do start in production
mode by default. So in our case we now need only one volume for the Ghost content
(depending on how you handle config files ... see below).
IMPORTANT: The path in the container has changed from /var/lib/ghost
to now /var/lib/ghost/content
... you might be tempted to leave it the old way (e.g. in order to have the config.production.json
within the volume) ... DON'T do that:
- In my experiences Ghost did not find the paths to your images in your blog posts
- More importantly Ghost re-initiated the initial setup (each time) after a removal and restart of the docker container/docker stack. I'm not sure how this is related but it happened to me always with the old path and reliably did not happen when using the new path.
- The new path explicitly is marked as Breaking Change in Ghost's Docker Hub repo description.
How to persist the config file
Despite being no longer in the docker volume
in order to maintain your individual config across restarts (e.g. a URL with sub-domains or paths after the host part) you can come up with something rather unconventional like
# Dockerfile
<...>
RUN sed -i "s|localhost:2368\/|yourDomain.com\/yourPath\/|g" config.production.json
<...>
or use something more appropriate such as the newly introduced docker configs
(Docker version 17.06+):
# docker-compose.yml (full sample below)
<...>
configs:
- source: config_production
target: /var/lib/ghost/config.production.json
uid: "1000"
gid: "1000"
mode: 0440
<...>
These docker config
work pretty much as docker secrests
do. Full files can be found at the end of this post.
You might as well just stick with "old school plane jane" docker
and use instead a simple COPY
instruction in your Dockerfile.
Using users
The previous versions' user called user
has been dropped. But you can now use the user node
from the NodeJS
base image by adding a simple USER node
in your dockerfile
.
This (repeatedly) worked out of the box and no (more) need for crazy RUN chown -R ...
statements.
The actual import
The actual import of the previously exported JSON worked pretty reliable every time I did it:
Users, posts and settings
When you take identical credentials in the new Ghost's setup process Ghost does not import that user (and gives a warning) but the re-imported posts are automatically assigned to you. This at least worked if I left the initial default Ghost blog posts, imported the JSON and then only after that deleted the default ghost stuff (stories, user, tags, etc.).
Otherwise (all users imported and all posts assigned as before) imported users are locked on import and have to reclaim access via the email-based password reset mechanism.
Even code injection
and other genereal settings
were nicely imported and directly working after the import.
Images
After the JSON import you still need to copy
or tar
your <your_whatever_old_base_path>/content/images
to now /var/lib/ghost/content/images
in your new docker volume
(so copy from where your images really are ... this is one of the parts with Ghost's 0.x versions where initially starting in development
and then switching to production
used to be also a switch from /usr/src/ghost
to /var/lib/ghost
).
But now everything works as expected: Even a docker cp /path/on/host <container_id>:/path/in/container/
worked automatically with correct file permissions ... even for the user node
.
Default theme Casper
As expected the old default theme Casper was not imported (this gave another warning). You can stick with the default new Casper theme or download version 1.4 which is the latest version optimized for Ghost 1 and above:
- Importing and successfully activating it reliably worked every time.
- You may need to (re-)upload your cover, logo and the new publication icon.
- This publication icon e.g. sets the favicon for your blog.
After that your blog should be up and running again.
Database
Worth a note: The official Docker image still uses SQlite for data persistence. I did not change it.
Editing and Handling
New editor is nice (but on the other hand did not blow me away either) and tidied up. You can now toggle between full-screen and side-by-side mode. And you do have a built-in spell-checker (which works great and is a major add-on but which ironically did not recognize the word "blog" ;-> ).
Some shortcuts and markdown have changed:
- On Mac OS I could not use the shortcut for
inline code
as it is already occupied by Safari - Headings now need to be
##Heading
... the old markdown##Heading##
in the new version is still rendered as heading but also reveals the hash tags on the right side of the heading## content. - The are some others mentioned as "Markdown breaking changes" in the migration guide
4. Sample Files
In the following some sample files for how you could do it ... they should be working but should also be considered only as a basic starting point of your container configuration:
Dockerfile Example (basic)
FROM ghost:latest
RUN apt-get update
RUN apt-get autoremove -y \
&& apt-get autoclean -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
# COPY or RUN sed your config if needed
# or just take the docker configs as below
USER node
# NODE_ENV already set to "production" by default
Docker-Compose.yml Example (basic)
version: "3.3"
services:
<ghost_service>:
<...>
volumes:
- <docker_volume_name>:/var/lib/ghost/content
<...>
configs:
- source: config_production
target: /var/lib/ghost/config.production.json
uid: "1000"
gid: "1000"
mode: 0440
environment:
- NODE_ENV=production # already set ... but just to be sure.
<...>
volumes:
<docker_volume_name>:
external: true
configs:
config_production:
file: ./local/path/to/file/config.production.json
<...>
5. Conclusion
Go ahead ... give it a try ... the beauty about Docker is that you can easily test it (several times) in one of your testing or development environments and then optimize the steps necessary and move to production.
I will update this post as soon as I have finally moved also within the production
environment if there have been other effects not mentioned here yet.
6. Further Information
Ghost Docs & Docker Hub:
https://docs.ghost.org/docs/migrating-to-ghost-1-0-0
http://api.ghost.org/docs/getting-started#section-migrating-to-ghost-1-0-0
https://hub.docker.com/_/ghost/
Ghost Markdown Guide
https://help.ghost.org/hc/en-us/articles/224410728-Markdown-Guide
Casper Theme 1.4:
https://github.com/TryGhost/Casper/releases/tag/1.4.0
Ghost & NodeJS official Dockerfiles:
https://github.com/docker-library/ghost/blob/4b5a3383906e72f645f3f68a8e8f9a20cd26338e/1/debian/Dockerfile
https://github.com/nodejs/docker-node/blob/17c50cb300581280805a4183524fbf57840f3a7e/6.11/slim/Dockerfile
Docker Configs Example:
https://docs.docker.com/engine/swarm/configs/#simple-example-get-started-with-configs