Skip to main content
Version: 0.5.3

Ghost on Aruba Cloud

Deploy a production-ready Ghost blog on Aruba Cloud using Terraform and cloud-init. No manual server configuration required.

Provider version: arubacloud/arubacloud ~> 0.5 | Terraform: ≥ 1.9


Introduction​

Ghost is a modern open-source publishing platform built with Node.js, designed for professional bloggers, newsletters, and membership sites. This example provisions a complete Ghost stack on Aruba Cloud with:

  • A CloudServer VM running Node.js 20 and Ghost served behind nginx, fully bootstrapped by cloud-init
  • A Managed MySQL 8.0 DBaaS instance — no self-managed database server
  • A dedicated VPC, subnet, and security groups via the shared network module
  • Elastic IPs for the VM and DBaaS
  • Optional Let's Encrypt HTTPS when a custom domain is provided

The Ghost admin account is created on first browser visit to /ghost — no passwords are set during provisioning.


Architecture Overview​

Ghost listens on port 2368 and is proxied by nginx on port 80/443. The database runs on a separate managed DBaaS instance in the same VPC. The MySQL security group allows inbound connections only from the VM's Elastic IP.


Infrastructure Created​

ResourceName patternDescription
arubacloud_projectghost-prodProject container
arubacloud_vpcghost-prod-vpcVirtual Private Cloud
arubacloud_subnetghost-prod-subnetBasic subnet
arubacloud_securitygroupghost-prod-vm-sgVM security group
arubacloud_securitygroupghost-prod-db-sgDBaaS security group
arubacloud_securityruleghost-prod-vm-sshSSH ingress (restricted CIDR)
arubacloud_securityruleghost-prod-vm-httpHTTP ingress (0.0.0.0/0)
arubacloud_securityruleghost-prod-vm-httpsHTTPS ingress (0.0.0.0/0)
arubacloud_securityruleghost-prod-db-mysqlMySQL ingress from VM IP only
arubacloud_elasticipghost-prod-vm-eipVM public IP
arubacloud_elasticipghost-prod-db-eipDBaaS public IP
arubacloud_blockstorageghost-prod-boot30 GB boot disk (Performance)
arubacloud_keypairghost-prod-keypairSSH public key
arubacloud_dbaasghost-prod-dbaasManaged MySQL 8.0
arubacloud_databaseghostGhost logical database
arubacloud_dbaasuserghostMySQL application user
arubacloud_databasegrant—liteadmin grant
arubacloud_cloudserverghost-prod-vmCloudServer VM

VM Sizing Recommendation​

WorkloadvCPURAMDiskFlavor
Personal blog / newsletter24 GB30 GBCSO2A4 (default)
High-traffic / membership site48 GB40 GBCSO4A8

For the DBaaS: DBO2A8 (2 vCPU / 8 GB) covers most Ghost sites. Ghost is Node.js-based and is more memory-efficient than PHP stacks, so the 2 vCPU / 4 GB VM tier handles typical blog and newsletter workloads well.


Estimated Monthly Cost​

Approximate prices for ITBG-Bergamo, hourly billing. Actual prices may vary — verify in the ArubaCloud console.

ResourceSpecEst. cost/mo
CloudServer VMCSO2A4 — 2 vCPU / 4 GB~€18
Boot disk30 GB Performance~€4
Managed MySQLDBO2A8 — 2 vCPU / 8 GB~€35
DBaaS storage20 GB~€3
Elastic IP × 2—~€5
Total~€65/mo

Requirements​

  • Terraform ≥ 1.9
  • ArubaCloud Terraform Provider ~> 0.5
  • An ArubaCloud account with OAuth2 API credentials
  • An SSH key pair

Variables​

Required​

VariableDescription
arubacloud_client_idArubaCloud OAuth2 client ID
arubacloud_client_secretArubaCloud OAuth2 client secret
ssh_public_keySSH public key content (e.g. contents of ~/.ssh/id_ed25519.pub)
db_passwordMySQL password for the Ghost user (min 16 chars, no newlines)

Optional​

VariableDefaultDescription
app_name"ghost"Short name used in all resource names
environment"prod"Environment label (prod, staging, dev)
location"ITBG-Bergamo"ArubaCloud region
zone"ITBG-1"Availability zone
billing_period"Hour""Hour" or "Month"
vm_flavor"CSO2A4"CloudServer flavor
vm_image"LU22-001"Boot disk image (Ubuntu 22.04 LTS)
vm_disk_size_gb30Boot disk size in GB
ssh_cidr"0.0.0.0/0"CIDR for SSH access — restrict to your IP in production
dbaas_flavor"DBO2A8"DBaaS flavor
db_storage_gb20DBaaS initial storage in GB
domain""Custom domain for HTTPS — leave empty to use the Elastic IP

Deployment Instructions​

1. Clone and navigate​

git clone https://github.com/arubacloud/terraform-arubacloud-examples.git
cd terraform-arubacloud-examples/ghost

2. Configure variables​

cp terraform.tfvars.example terraform.tfvars

Edit terraform.tfvars with your credentials and database password.

Tip: Store credentials as environment variables to avoid writing them to disk:

export TF_VAR_arubacloud_client_id="your-id"
export TF_VAR_arubacloud_client_secret="your-secret"

3. Initialize and deploy​

terraform init
terraform plan # review the execution plan
terraform apply

4. Access Ghost​

After apply completes (typically 10–15 minutes for cloud-init):

terraform output site_url # e.g. http://203.0.113.10
terraform output ghost_admin_url # e.g. http://203.0.113.10/ghost

Open the admin URL in your browser. The first visit registers your admin account — fill in your name, email, and password. This is the only user created during setup.

5. Follow cloud-init progress (optional)​

While cloud-init runs, you can tail the bootstrap log:

ssh ubuntu@$(terraform output -raw vm_public_ip)
sudo tail -f /var/log/cloud-init-output.log

Destroy Instructions​

terraform destroy

This removes all created resources. The DBaaS data is destroyed — take a snapshot first if you need to preserve it:

# Take a DBaaS backup before destroying (manual step via console or API)
terraform destroy

Security Recommendations​

  1. Restrict SSH to your IP. Set ssh_cidr = "your.ip.address/32" in terraform.tfvars. The default 0.0.0.0/0 is for getting-started convenience only.

  2. Use a custom domain with HTTPS. Set the domain variable. Certbot will automatically provision and renew a Let's Encrypt certificate.

  3. Choose a strong admin password. When registering the admin account on first visit, use a unique password of at least 16 characters.

  4. Keep Ghost updated. SSH into the VM and run ghost update --dir /var/www/ghost as the ghost user (see Upgrade Considerations below).

  5. Do not expose MySQL publicly. The DBaaS security group already restricts MySQL to the VM's IP. Do not add 0.0.0.0/0 ingress rules to the DBaaS security group.


Upgrade Considerations​

Ghost version upgrades​

Ghost CLI handles in-place upgrades:

ssh ubuntu@$(terraform output -raw vm_public_ip)
sudo -u ghost ghost update --dir /var/www/ghost

Always review the Ghost changelog for breaking changes before upgrading.

Node.js version upgrade​

Change the NodeSource setup script URL in cloud-init.yaml.tpl (e.g. setup_22.x) and trigger a VM replacement by modifying user_data. Run terraform apply to replace the instance with a fresh bootstrap.

Provider upgrade​

When the provider releases a new minor version, update the constraint in versions.tf and run terraform init -upgrade. Always review the provider CHANGELOG before upgrading.


Screenshots​

Screenshot placeholder. After deployment, add screenshots of the Ghost front page and admin dashboard here.

Admin dashboardFront page
(screenshot)(screenshot)

Login Credentials After Deployment​

Ghost does not set an admin password during provisioning. On first access, navigate to the admin URL and register your account.

ServiceURLUsernamePassword
Ghost Admin$(terraform output ghost_admin_url)(set on first visit)(set on first visit)
MySQL$(terraform output dbaas_host):3306ghost$(terraform output -raw db_password)
SSH$(terraform output ssh_command)ubuntuSSH key

Troubleshooting​

Ghost is not reachable after apply​

  1. cloud-init still running. Ghost installation takes 10–15 minutes after VM boot. Check the log:

    ssh ubuntu@$(terraform output -raw vm_public_ip)
    sudo tail -f /var/log/cloud-init-output.log
  2. DBaaS not ready yet. cloud-init waits up to 15 minutes for MySQL. If the wait timed out, check the DBaaS status in the console and restart cloud-init manually.

  3. Ghost service not started. Verify Ghost is running:

    sudo -u ghost ghost status --dir /var/www/ghost

nginx returns 502 Bad Gateway​

Ghost is not running or listening on port 2368:

sudo -u ghost ghost status --dir /var/www/ghost
sudo -u ghost ghost start --dir /var/www/ghost

Certbot fails to issue a certificate​

  • DNS must resolve the domain to the VM's Elastic IP before terraform apply.
  • Certbot requires ports 80 and 443 to be reachable. Verify your security group rules.
  • Check /var/log/letsencrypt/letsencrypt.log for details.

cloud-init bootstrap did not complete​

ssh ubuntu@$(terraform output -raw vm_public_ip)
sudo systemctl status cloud-init
sudo cat /var/log/cloud-init-output.log

Look for the final_message near the end of the log. If missing, scroll up to find the error.

Plan errors: "resource name already exists"​

A previous terraform destroy may not have completed. Either finish destroying or change app_name / environment to use a different resource name prefix.


References​