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.

    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
    

    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.

    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.

    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.

    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.

    Back up and restore

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

    Method 1: Use SFTP to bacup Ghost

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

    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.

    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/.

    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.

    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.

    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