Migrate Ubuntu Ghost instance to Docker Container using Docker Compose and Docker File

This step-by-step guide walks you through utilizing Docker Compose and Dockerfile to migrate your Ghost instance, streamlining management and enhancing scalability

Migrate Ubuntu Ghost instance to Docker Container using Docker Compose and Docker File

Lately, I've been exploring Docker Containers and decided to transition my instance to one. This simplifies rebuilding and streamlines administration tasks. In this guide, I've utilized Docker Compose and Dockerfile to construct the containers.

My current(existing) ghost configuration

  • Ubuntu 22.4
  • Systemd
  • Node
  • Systemd
  • MySQL 8

This is the guide I followed: https://ghost.org/docs/install/ubuntu/

Note, that this isn't an official guide so your results may vary. Please backup your ghost instance in the event something breaks.

Create a new directory

First, you want to create a new directory for your files (docker-compose.yml & Dockerfile). These files will be utilized collectively to enable you to redeploy your instance whenever necessary.


mkdir phiptech  && cd phiptech

Backup your Ghost instance

Backup Ghost Content

Next, you'll want to back up the content for Ghost. Navigate to where you have configured your Ghost installation and type the command below:

ghost backup

Once you have a backup, it should be saved in the backup directory as shown below. Note down this directory for later

e.g Backup directory

Extract these files to the directory created above. e.g. phiptech/content

Backup Database

The next part of the backup process is to also backup the SQL database. This can be done by taking an SQL dump from the existing SQL database

    1. Navigate to /home/ghost/mysql (default directory)
    2. Run command :
sudo mysqldump  --all-databases > ~/all_backups.sql

Copy SQL File & Ghost Content Folder

Extract the backup files using the unzip command in the new directory you created above if you haven't already done so

Create SQL Script file called grant_privileges.sql and copy/paste the below. You will need to find the database username that you used initially when you set up ghost. The below script will be used to grant the existing user access to the new SQL instance

CREATE USER 'ghost-658'@'%' IDENTIFIED BY '';
GRANT ALL PRIVILEGES ON phiblog_prod.* TO 'ghost-658'@'%';


CREATE USER 'ghost-658'@'%' IDENTIFIED BY '<yourpassword>';

    • This line creates a new MySQL user named 'ghost-658' with a password specified as <yourpassword>. The '%' wildcard in the @'%' part allows this user to connect from any host.

GRANT ALL PRIVILEGES ON phiblog_prod.* TO 'ghost-658'@'%';

    • This line grants all privileges (SELECT, INSERT, UPDATE, DELETE, etc.) on all tables within the phiblog_prod database to the user 'ghost-658' when connecting from any host ('%').
    • The .* after phiblog_prod specifies that the privileges apply to all tables within the phiblog_prod database.
SQL Error

Build Docker Compose file

Following this, we'll leverage Docker Compose to instantiate the containers. This approach enables us to define and manage multiple containers, providing a seamless deployment process.

Create the Docker Compose File

version: "3.3"
    image: ghost:latest
    restart: always
      - "6060:2368"
      url: https://phiptech.com
      database__client: mysql
      database__connection__host: mysql
      database__connection__database: phiblog_prod
      database__connection__user: ghost-658
      database__connection__password: "${MYSQL_PASSWORD}"
      database__connection__port: 3306
      - ghost-data:/var/lib/ghost/content
      - /home/phi/repos/phiptech/content:/var/lib/ghost/backup/content

    command: ["bash", "-c", "cp -r /var/lib/ghost/backup/content/* /var/lib/ghost/content"]

      - mynetwork
      context: .
      dockerfile: Dockerfile
    restart: always
      MYSQL_DATABASE: phiblog_prod
      - mynetwork
    - ghost-db-prod:/var/lib/mysql

    driver: bridge

What you need to edit:

  • url: Your website URL.
  • ports: Maps ports between the container and the host machine. In this case, it's mapping the port 2368 from the container to the port 6060 on the host machine. You can access this viahttp://localhost:6060 on your host machine.
  • `database values: change as required.
  • "${MYSQL_PASSWORD}": This part uses environment variable substitution. It's pulling the value of an environment variable named MYSQL_PASSWORD and inserting it into the configuration at this point. So, the password for the database connection will be whatever value is stored in the MYSQL_PASSWORD environment variable when the application or service using this configuration file is run. We'll edit the environment variables below.
  • "${MYSQL_ROOT_PASSWORD}": same as above
  • The volume section: ghost-data that will be mounted to the /var/lib/ghost/content directory within the container. Volumes are a way to persist data generated by and used by Docker containers. This means you won't lose data when the container is deleted.
  • networks:
    driver: bridge

    This section defines a Docker network named mynetwork.The network is set to use the bridge driver, which is the default network driver for Docker. This is useful if you want other containers to talk to each other. In this case, the 2 containers will be on the same network so they can communicate.
  • volumes:
    • For clarity, we are defining the volumes as we did above, the same with networks. You can leave it as is


    1. This line maps the local directory /home/phi/repos/phiptech/content on the host machine to the directory /var/lib/ghost/backup/content inside the Docker container.

command: ["bash", "-c", "cp -r /var/lib/ghost/backup/content/* /var/lib/ghost/content"]

    1. This specifies the command to run when the container starts. It executes a bash command (bash -c) within the container.
    2. The command cp -r /var/lib/ghost/backup/content/* /var/lib/ghost/content copies all files and directories from /var/lib/ghost/backup/content to /var/lib/ghost/content within the container. This is typically used for data migration or initialization tasks, ensuring that the necessary files are in place for the application to function correctly. (This is a once-off task) Please comment this line out when you first start your instance

Create a Docker File for the SQL Container build

# Use the official MySQL image from Docker Hub
FROM mysql:8.0

# Copy the MySQL dump file into the container
COPY all_backups.sql /docker-entrypoint-initdb.d/all_backups.sql

# Expose MySQL port (optional)

# Copy the SQL script to grant privileges
COPY grant_privileges.sql /docker-entrypoint-initdb.d/

# Set the network mode to bridge
CMD ["mysqld", "--bind-address="]


FROM mysql:8.0: Pulls the official MySQL 8.0 image from Docker Hub.

COPY all_backups.sql /docker-entrypoint-initdb.d/all_backups.sql: This copies a SQL dump file named all_backups.sql from the host into the Docker container. This file is backup you created above.

The destination /docker-entrypoint-initdb.d/ is a directory where MySQL automatically executes SQL scripts during container initialization.

EXPOSE 3306: This exposes port 3306, the default port used by MySQL, to allow communication with the MySQL server from other containers or the host system. Note that this does not publish the port to the host machine; it's mainly used for inter-container communication.

COPY grant_privileges.sql /docker-entrypoint-initdb.d/: This instruction copies another SQL script named grant_privileges.sql into the same directory /docker-entrypoint-initdb.d/. This script likely contains SQL commands to grant privileges to MySQL users.

CMD ["mysqld", "--bind-address="]: This specifies the command to run when the container starts. Here, it runs the MySQL daemon (mysqld) with the --bind-address= option, allowing MySQL to accept connections from any IP address.

Create environments file (.env)

Adjust the password values. These environment variables were previously defined in the docker-compose file. It's a recommended practice, particularly for security reasons, to exclude passwords from git commits. You can extend this practice to other sensitive variables, including database details. For now, let's focus on handling password variables for simplicity.


Start Docker Container

Before you create the container. Ensure that you aren't using the same ports for the existing ghost instance. You will need to stop the service if you are.

Now that you have all the files required, enter the below command to start the container. This should take a few seconds to start the container

sudo docker-compose up -d

Monitor Docker Container Logs

You will need to monitor the logs as you will get errors. I've pasted several images that I received during the process. These are normal errors when it is started. Continue monitoring the logs until the very end

sudo docker logs  --follow

After waiting 30 seconds to 1 minute

You want to take note of the highlighted red boxes in the below screenshot. This means the SQL script "grant_privileges.sql" and the SQL Back up "all_backups.sql" are running properly. If this does not run, please recheck all files are there.

When you see "ready for connections" this means it is now up and running. Navigate to your website to confirm.

And there we have it, you've migrated your ghost instance to containers.

Useful commands

docker ps - List containers on your host

sudo docker-compose down --remove-orphans This command stops and removes containers, networks, volumes, and images created by docker-compose up. Use this command if you want to remove your container

--remove-orphans flag removes containers for services not defined in the Compose file. Orphan containers are those that are not associated with a service in the current Compose file.

sudo docker-compose up -d

    • This command rebuilds and starts the containers defined in the docker-compose.yml file in detached mode (-d flag), which means it runs containers in the background.
    • It recreates the containers if changes have been made to the configuration or if the containers were stopped.

Reset SQL root password

You may want to reset your root password if you have forgotten it like I did, see snippets below for instructions to reset

  1. Run comamnds
sudo mkdir -p /var/run/mysqld
sudo chown mysql:mysql /var/run/mysqld
sudo mysqld_safe --skip-grant-tables &

  1. Login SQL
mysql -u root
  1. Reset password
ALTER USER 'root'@'localhost' IDENTIFIED BY '';

SET PASSWORD FOR 'root'@'localhost' = PASSWORD('');
  1. Exit safe mode
ps aux | grep mysqld_safe
check process
exit safe mode
  1. Login with new password
mysql -u root -p
  1. Backup databases
sudo mysqldump  --all-databases > ~/all_backups.sql

Understanding Self-Hosted Ghost Data Structure | vkhitrin.com

Updating your Ghost Docker Container to MySQL8 (pointtosource.com)

How to reinstall Ghost

Self Hosting a Ghost blog with docker-compose, Mailgun setup, and Stripe subscriptions (ajfriesen.com)

Host ’′ is not allowed to connect to this MySQL server · Issue #275 · docker-library/mysql
I’m trying to connect to the MySQL by the DBeaver db manager. But I get this error: java.sql.SQLException: null, message from server: “Host ’′ is not allowed to connect to this MySQL serv…