3 min read

Guide: Ghost blog on Fly.io using Tigris S3 Object Storage (part 3)

Guide: Ghost blog on Fly.io using Tigris S3 Object Storage (part 3)

This is part 3 in a series about self hosting a Ghost blog on Fly.io. It assumes you have a running Ghost app on Fly (either using SQLite or MySQL).

In the first two posts we covered:

  1. Setting up a Ghost blog using SQLite
  2. Setting up a Ghost blog using MySQL

Now we'll move onto saving assets (in particular photos that you upload to your blog posts) in a S3 Object Storage bucket.

This is what we're aiming for:

Tigris is awesome

Tigris Global Object Store is a s3 compliant storage system built on Fly's infrastructure. We'll be connecting it to our Ghost Blog. For more details:

Tigris Global Object Storage
Documentation and guides from the team at Fly.io.

Step 1: Create a Dockerfile

If you've been following the guides up until this point then in your fly.toml file you might have the line:

# fly.toml

# erase this!
[build]
  image = 'ghost:5.81.0'

But in addition to our Ghost image, we also need an Ghost s3 storage library. So, remove the image line from the fly.toml and create a Dockerfile that looks like

FROM ghost:5.81.0-alpine
WORKDIR /var/lib/ghost
RUN npm install --prefix /tmp/ghos3 ghos3 && \
  cp -r /tmp/ghos3/node_modules/ghos3 current/core/server/adapters/storage/s3 && \
  rm -r /tmp/ghos3

RUN npm install ghos3 && npm install aws-sdk

This Dockerfile was written by underlost here: https://github.com/underlost/flyio-ghost-s3

I've modified it to use ghos3 which is a recent rewrite of ghost-storage-adapter-s3.

Step 2: Create a Tigris Object Storage Bucket

You can basically follow the guide for Tigris, but the short version is:

fly storage create <my_bucket_name> --public

You'll get some credentials that you'll want to keep for later:

BUCKET_NAME: my_bucket_name
AWS_ENDPOINT_URL_S3: https://fly.storage.tigris.dev
AWS_ACCESS_KEY_ID: tid_xxxxxx
AWS_SECRET_ACCESS_KEY: tsec_xxxxxx

Step 3: Modify fly.toml and set secrets

Add the following to you fly.toml under [env]

# fly.toml
# don't forget to replace "my_bucket_name" with your own name

[env]
  ##### Tigris S3
  storage__active = "s3"
  # storage__s3__accessKeyId = "" # set as secret
  # storage__s3__secretAccessKey = "" # set as secret
  storage__s3__region = "auto"
  storage__s3__bucket = "my_bucket_name"
  storage__s3__assetHost = "https://fly.storage.tigris.dev/my_bucket_name"
  storage__s3__endpoint = "https://fly.storage.tigris.dev"
  • region is auto
  • the asset host (storage__s3__assetHost) needs the bucket name in the path
  • for uploading (storage__s3__endpoint) should omit the bucket name

Next, set secrets for your app using the credentials from when you made the bucket:

fly secrets set --stage storage__s3__accessKeyId=tid_xxxx storage__s3__secretAccessKey=tsec_xxx

Step 4: Deploy and check that it works:

Run fly deploy to deploy your app.

Then create a draft blog post and try uploading an image.

You can see the image in your bucket by running the following:

flyctl storage dashboard <bucket_name>

This should open up Tigris' dashboard and you can browse the files in your bucket. It's neato burrito.


Final Example:

Putting it all together with MySQL and Tigris Object Storage your fly.toml might look like:

# fly.toml app configuration file generated for nathan-ghost-blog on 2024-07-13T22:34:38+09:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = 'my-ghost-blog'
primary_region = 'sea'

[env]
  ##### MYSQL Database
  database__client = 'mysql'
  # database__connection__user = '' # set as secret
  # database__connection__password = '' # set as secret
  database__connection__host = 'my-blog-db.internal'
  database__connection__port = '3306'
  database__connection__database = 'blog_db'
  database__connection__useSSL = 'false'
  database__useSSL = 'false'
  ##### Tigris S3
  storage__active = "s3"
  # storage__s3__accessKeyId = "" # set as secret
  # storage__s3__secretAccessKey = "" # set as secret
  storage__s3__region = "auto"
  storage__s3__bucket = "my-blog-storage"
  storage__s3__assetHost = "https://fly.storage.tigris.dev/my-blog-storage"
  storage__s3__endpoint = "https://fly.storage.tigris.dev"
  url = 'https://my-ghost-blog.fly.dev'

[http_service]
  internal_port = 2368
  force_https = true
  auto_start_machines = true
  min_machines_running = 0
  processes = ['app']

[[vm]]
  memory = '1gb'
  cpu_kind = 'shared'
  cpus = 1