Ghost on Fly.io for Free: Install, Backup and Restore

This article introduce how to run a free ghost blog on fly.io to backup and restore data. Backing up and restoring data is informative for use cases on other servers.
Table of Contents

This article introduce how to install, backup and restore ghost on fly.io for free. Backing up and restoring data is a reference for use cases on other servers. With the use of cron, the full site data is regularly backed up to github, so to speak, you can really feel at ease with ghost and give up static blogs.

1. Pre-requisites

  1. To register for a fly.io account,install flyctl command line, you need a credit card. (required)
  2. be familiar with basic git commands. (not required)
  3. Understand fly.io's pricing. (not required)
  4. Understand Mailgun. (not required)

Install the command line

curl -L https://fly.io/install.sh | sh

For Windows,run

iwr https://fly.io/install.ps1 -useb | iex

2. Install Ghost

The following command is common on Linux and Mac.

Open a terminal and execute.

mkdir blog #Create a directory
cd blog #Enter this directory
flyctl auth login #Log in, then the browser will pop up the login session
flyctl launch --image=ghost:5 -r hkg --name=<AppName> --no-deploy

An explanation of the last command:

  • The docker image used is ghost:5, indicating that the 5.x major version will be updated automatically on every deployment, and if you intend to check a specific version, such as 5.36.0, then replace 5 with it. The official image is here.

  • - r hkg means that the node of the selected server is Hong Kong (the node closest to us). The region code of fly.io is here.

  • --name=<AppName> means the name of the created app, replace <AppName> with your own, and note that the default URL generated in the future is <AppName>.fly.dev, so please take a unique name.

  • --no-deploy means don't deploy for now.

This will create a fly.toml file in the blog directory. Open it with a text editor and overwrite it with the following code.

app = "<AppName>"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []

[build]
  image = "ghost:5"

[env]
  url = "https://<AppName>.fly.dev"
  database__client = "sqlite3"
  database__connection__filename = "content/data/ghost.db"
  database__debug = "false"
  database__useNullAsDefault = "true"
  mail__from = "noreply@example.com"
  mail__options__auth__pass = "<YourMailgunPassword>"
  mail__options__auth__user = "postmaster@example.com"
  mail__options__host = "smtp.mailgun.org"
  mail__options__port = "465"
  mail__transport = "SMTP"

[experimental]
  auto_rollback = true

[mounts]
  destination = "/var/lib/ghost/content"
  source = "data"

[[services]]
  http_checks = []
  internal_port = 2368
  processes = ["app"]
  protocol = "tcp"
  script_checks = []
  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    force_https = true
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

    [services.ports.http_options.response.headers]
      Referrer-Policy = "strict-origin"
      Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload"
      X-Content-Type-Options = "nosniff"
      X-Frame-Options = "SAMEORIGIN"
      x-xss-protection = "1; mode=block"
      Permissions-Policy = "camera=(), microphone=(), geolocation=(), browsing-topics=()"

  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

Please replace <AppName> with your own, and, if you don't use Mailgun, please remove all code starting with mail__ below [env].

The [services.ports.http_options.response.headers] section is optional and is used to set the http header. You can search item by item, Strict-Transport-Security, for example, to determine if you want to add them. After the deployment, you may go to the http header test site.

When you are done checking, save and close the fly.toml file. Then go back to the terminal and continue executing.

  flyctl volumes create data -r hkg --no-encryption --size 1 # Create a volume of size 1Gb with region hkg and no encryption (helps improve performance). The free amount is 3GB in total.
  flyctl deploy #deploy

Wait a few moments, the deployment is successful! It will show successful.

3. Initialize Ghost

Visit

https://<AppName>.fly.dev/ghost/

to initialize ghost. After this, you are free to use ghost to publish content. Binding domain names, backing up and restoring data are optional operations.

4. Bind the domain name

Run

  flyctl ips list

to see the IP address of your app. Then create A records for ipv4 and AAAA records for ipv6 on your DNS server, pointing to your domain name example.com (required). Create CNAME, <AppName>.fly.dev, pointing to your www.example.com (not required). Also known as

typerecordvalue
A@ipv4 address
AAAAA@ipv6 address
CNAMEwww<AppName>.fly.dev

Note: Before binding the domain name, make sure to create the DNS records correctly.

Then run

flyctl certs create example.com
flyctl certs create www.example.com

wait a moment and run

flyctl certs check example.com
flyctl certs check www.example.com

to check the certificate enactment, which usually takes about a minute or two as long as the DNS advance configuration is correct. Run

flyctl ips list

to check the certificate issuance status.

Note: Don't install certificates repeatedly because there is a limit, only 5 certificates can be issued every 7 days. It is recommended to bind them after debugging the app.

5. Redeploy

Notice that in the fly.toml,

[env]
  url = "https://<AppName>.fly.dev"

is the address assigned by fly.io, and if you click on the home page title of the ghost site, it will redirect to this address, so when you bind your own domain, change it to

[env]
  url = "https://example.com"

Save and close, then run flyctl deploy.

6. Back up and restore

Every time in the future you need to go into your local configuration directory to use flyctl correctly.

6.1 Method 1: Use SFTP to bacup Ghost

The advantage is that no complicated configuration is needed, but the copy speed is slow.

6.1.1 Back up

First login (assuming you need to back up later).

cd blog
flyctl auth login

Then run

flyctl ssh sftp shell -r -a <AppName>

When it succeeds, it will show >> (red), then run

get /var/lib/ghost/content/

This will backup the whole site data, including themes, images, videos, articles, etc. It's very slow, please be patient.

You can also backup only images and videos by running

get /var/lib/ghost/content/images/
get /var/lib/ghost/content/media/

This will generate a content.zip, or, images.zip and media.zip in the blog directory. back them up elsewhere, or name them content-YYYY-MM-DD.zip.

6.1.2 Restore

Now suppose you reinstall a new ghost, assuming it is still on fly.io, then the installation directory will remain as /var/lib/ghost/content/. If you install ghost somewhere else in the future, like on AWS, make sure you create this directory instead of the usual tutorial /var/www/ghost/content/, otherwise you will have trouble recovering your data by first method.

Still run

cd blog
flyctl auth login
flyctl ssh sftp shell -r -a <AppName>

Then run

put content.zip /var/lib/ghost/content.zip

After success, create a new tab in the terminal and run

flyctl ssh console

again, in ssh, run

apt-get update && apt-get upgrade && apt-get install unzip

to install unzip. Then run

cd /var/lib/ghost/
unzip content.zip

It will overwrite the content folder directly.

To verify that the unzip was successful, run

cd /var/lib/ghost/content/images/
ls #List all the files in the current folder

to see if there is any new data.

To fix permissions, run

chown -R node:node /var/lib/ghost/content/

Note: Possible scenario, can't overwrite the content folder correctly, then try to backup only /var/lib/ghost/content/images/ and /var/lib/ghost/content/media/ instead. At this point run

put images.zip /var/lib/ghost/content/images.zip
put media.zip /var/lib/ghost/content/media.zip

rm -rf /var/lib/ghost/content/images/
rm -rf var/lib/ghost/content/media/

unzip /var/lib/ghost/content/images.zip
unzip /var/lib/ghost/content/media.zip

this may break the permissions of the content folder, so run

ls -l /var/lib/ghost/content/

to check the user and permissions of the images and media folders, if their permissions belong to root, then you can't upload images and videos anymore, then run

chown -R node:node /var/lib/ghost/content/
chown -R node:node /var/lib/ghost/content/images/
chown -R node:node /var/lib/ghost/content/media/

to fix it, note that node is the ghost user in the fly.io case, depending on the result of the ls -l /var/lib/ghost/content/ command,, in any case make sure that the user of the images and media folders is the same as the user of the other files under /content/.

6.2 Method 2: Use Github to backup Ghost

The advantage is that it is fast to replicate data, but the disadvantage is that the installation and configuration is relatively complicated.

6.2.1 Back up

  1. Login and enter ssh
cd blog
flyctl auth login
flyctl ssh console
  1. Install git
apt-get update && apt-get upgrade && apt-get install git
  1. Create a private (recommended) repository on the github web side , assuming it is called <YourRepository>.

  2. Generate the key

ssh-keygen -t rsa -C "<GithubEmail>" # Press the Enter key continuously
  1. Terminal new tab, run
flyctl ssh sftp shell -r -a <AppName>
get /root/.ssh/id_rsa.pub

Go back to the blog folder and find the id_rsa.pub file, open it with a text editor, and copy the key from it. Add it to setting→SSH and GPG keys→SSH keys in github and save it.

Test the connection with github

ssh git@github.com

Pay attention to the word successful.

  1. Initialize the content git
cd /var/lib/ghost/content/
git init
git config --global user.name "<GithubUsername>"
git config --global user.email <GithubEmail>
git config --global --add safe.directory /var/lib/ghost/content
git remote add origin git@github.com:<GithubUsername>/<YourRepository>.git
git add .
git commit -m "auto backup"
git push -u origin master --force

Make sure you successfully push to github.

  1. Create a new script
apt-get update && apt-get upgrade && apt-get install nano
cd /var/lib/ghost/content/
nano auto_run.sh

Copy the following code into auto_run.sh

#! /bin/sh

cd /var/lib/ghost/content # Switch to the directory
git config --global init.defaultBranch master
git config --global --add safe.directory /var/lib/ghost/content
git pull # Pull the repository
git add . # Add a staging
git commit -m "auto backup" # Commit
git push --force

Press ctrl+O followed by enter to write; press ctrl+x to exit nano.

Give the file permission to run

chmod a+x auto_run.sh

Later, after logging into ssh, you can directly run

sh /var/lib/ghost/content/auto_run.sh

to execute a new push.

6.2.2 Restore

  1. Log in to shh, reinstall git and add the key without further ado.

  2. Empty the content folder

cd /var/lib/ghost/
rm -rf content

It will return that the folder can't be deleted because its busy, it doesn't matter, it has been emptied, but not deleted.

  1. Initialize the git in the content file and force a pull
cd /var/lib/ghost/content/
git init
git config --global user.name "<GithubUsername>"
git config --global user.email <GithubEmail>
git pull --force git@github.com:<GithubUsername>/<YourRepository>.git
  1. Verify that the pull was successful
ls
  1. Make sure the data is successfully pulled, then restart the app
fly apps restart <AppName>

As of now, restarting or redeploying will clear the installed environment and software, but will not delete the ghost data.

  1. It is really difficult to run cron in docker, so don't try it. In a normal virtual machine, you can set up cron to run at regular intervals
sh /var/lib/ghost/content/auto_run.sh