svg

How to Deploy Ghost v5 and MySQL v8 on Fly.io

ghost mysql fly.io

Fly.io’s Hobby Plan provides a free allowance that lets you deploy up to three small machines (each with 1 shared CPU and 256 MB of RAM) and 3 GB of storage. So, you can try to create a shared-cpu-1x machine with 256 MB of RAM and 1 GB of storage for the ghost v5, and another shared-cpu-1x machine with 256 MB of RAM and 2 GB of storage for the MySQL v8. Deploying both Ghost v5 and MySQL v8 on such limited resources can be challenging, but it’s entirely achievable. This post walks you through the process step-by-step.

Step 1: Deploy MySQL v8

  1. Create a Working Directory
# Create a working directory for the Fly.io MySQL app
mkdir /home/sumar/Projects/fly.io-mysql
cd /home/sumar/Projects/fly.io-mysql
  1. Launch the Fly.io App for the MySQL Database
# Launch a new Fly.io app for the MySQL database
# --name specifies a unique app name (you can use any name or generate a random one)
# --region specifies the nearest region to deploy the app; here, we choose Singapore
# --org specifies the Fly.io organization
# This command generates a fly.toml file that we will edit later

flyctl launch --name kube-my-id-db --image=mysql:8.4.3 --vm-memory 256 --region sin --no-deploy --org personal
  1. Create a Volume for MySQL
# Create a 2GB volume for MySQL storage
# The volume will be used later as "db_data"
fly volumes create db_data --size 2 --region sin --auto-confirm
  1. Set MySQL Secrets
# Set MySQL user and root passwords
# The passwords can be generated using tools like pwgen
fly secrets set MYSQL_PASSWORD=aij1aiL0eim1Raqu
fly secrets set MYSQL_ROOT_PASSWORD=oath0Ci1Pievae7e
  1. Edit fly.toml Check your directory—it should now contain a fly.toml file. Edit it as shown below:
app = "kube-my-id-db"
primary_region = "sin"

# Adding swap memory to help manage the low RAM environment
swap_size_mb = 1_024

[build]
image = "mysql:8.4.3"

[env]
MYSQL_DATABASE = "ghost"
MYSQL_USER = "ghost"

[http_service]
auto_start_machines = true
auto_stop_machines = false
force_https = true
internal_port = 8_080
min_machines_running = 1
processes = ["app"]

[[mounts]]
# The source volume refers to the previously created "db_data"
destination = "/data"
source = "db_data"

[processes]
# This configuration ensures MySQL can run with only 256 MB of RAM
app = "--datadir /data/mysql --mysql-native-password=ON --performance-schema=OFF --innodb-buffer-pool-size 64M"

[[vm]]
size = "shared-cpu-1x"
memory_mb = 256
  1. Deploy MySQL
# Deploy the MySQL app
flyctl deploy

Monitor the logs during deployment to catch and address any errors.

Step 2: Deploy Ghost v5

  1. Create a Working Directory
# Create a working directory for the Fly.io Ghost app
mkdir /home/sumar/Projects/fly.io-ghost
cd /home/sumar/Projects/fly.io-ghost
  1. Launch the Fly.io App
# Launch a new Fly.io app for Ghost v5
# --name specifies a unique app name (you can use any name or generate a random one)
# --region specifies the nearest region to deploy the app; here, we choose Singapore
# --org specifies the Fly.io organization
# This command generates a fly.toml file that we will edit later
flyctl launch --name kube-my-id --image=ghost:5-alpine --vm-memory 256 --region sin --no-deploy --org personal
  1. Create a Volume for Ghost
# Create a 1GB volume for Ghost storage
flyctl volumes create ghost_data --region sin --size 1 --auto-confirm
  1. Set Secrets for Ghost
# Set the database connection password for Ghost
# Ensure this password matches the MySQL password set earlier
fly secrets set database__connection__password=aij1aiL0eim1Raqu
  1. Edit fly.toml

Edit the generated fly.toml file. Here’s an example configuration:

app = "kube-my-id"
primary_region = "sin"

# Adding swap memory to assist with low RAM usage
swap_size_mb = 512

[build]
image = "ghost:5-alpine"

[env]
NODE_ENV = "production"
database__client = "mysql"
database__connection__database = "ghost"
# The database host is the Fly.io MySQL app name with ".internal" as the TLD
# Adjust this value to match your MySQL app name
database__connection__host = "kube-my-id-db.internal"
database__connection__port = "3306"
database__connection__user = "ghost"
database__debug = "false"

# Custom domain for the blog
# Refer to <https://fly.io/docs/networking/custom-domain/> for details
url = "https://blog.kube.my.id"

# Optional email configuration
# mail__from = ""
# mail__options__auth__user = ""
# mail__options__host = ""
# mail__options__port = "587"
# mail__transport = "SMTP"

[http_service]
auto_start_machines = true
auto_stop_machines = false
force_https = true
internal_port = 2_368
min_machines_running = 1
processes = ["app"]

[[mounts]]
# The source volume refers to the previously created "ghost_data"
destination = "/var/lib/ghost/content"
source = "ghost_data"

[[vm]]
memory_mb = 256
size = "shared-cpu-1x"
  1. Deploy Ghost
# Deploy the Ghost app
flyctl deploy

Monitor the logs during deployment to catch and address any errors. Once the deployment is successful, you can access your Ghost blog at the specified URL if you have set up a custom domain and SSL certificates. Otherwise, you can access it at the Fly.io app URL.

Conclusion

With this setup, your Ghost v5 blog and MySQL v8 database are now deployed on Fly.io’s free tier shared-cpu-1x with 256MB of RAM. By optimizing resource usage (e.g., enabling swap and adjusting MySQL configurations), you can run both applications on minimal resources. Of course, this setup is not suitable for production environments with high load, but it’s a great way to experiment with deploying Ghost and MySQL on Fly.io. For production environments, consider upgrading to a higher-tier plan with more resources.

Ghost v5 and MySQL v8 RAM usage

Left is Ghost v5 and right is MySQL v8

My blog is running on this setup, no visitor except myself and some botnet scanning wordpress caugh. You can check it out at blog.kube.my.id.