October 9, 2020

Let me count the ways: how AWS loves Ghost

Let me count the ways: how AWS loves Ghost

Ghost is one of the most popular CMS and blog platforms, rated 2nd on GitHub, and the biggest open source headless CMS project. Ghost is an open source Node.js JAMstack implementation, and can be used headless, or with the built-in editor. Ghost can be self-hosted in different forms: installed using the ghost CLI on a single instance, or using a docker container.

I have been using Ghost for my blog for a few years, at no cost. I take advantage of the free tiers of different cloud providers to run Ghost, as well as Cloudflare as the CDN and to manage the domain. I really enjoy using, as its open source, lightweight, and easy to use. Creating and editing posts is easy using Markdown, or the built-in editor. I can even create content offline on my laptop, push to git, to get posts into production. Since I run ghost as a docker container, upgrades are mostly pain-free: I started using version 0.9, and now currently on version 3.30.

AWS shows a lot love for WordPress, as I counted about 10 official AWS resources that talk about running WordPress on AWS: Implementation Guides, articles, tutorials, and even a whitepaper with best practices, using AWS services including Lightsail, Elastic Beanstalk and EC2. When I looked for official AWS docs on using Ghost on AWS, I only found AWS Marketplace images and a blog post on Lightsail.

So I decided to put together a whirlwind tour of multiple options to run Ghost using different AWS services, each offering varying levels of flexibility in terms of ease of use, ability to scale, and control.

Ghost architecture

Let's look at the Ghost architecture, which will guide us in choosing which AWS services to use. Ghost is a full headless CMS which is completely agnostic of any particular front end or static site framework. Ghost is structured as a modern, decoupled web application with a sensible service-based architecture.

  1. A robust core JSON API
  2. A beautiful admin client app
  3. A simple, powerful front-end theme layer

With the latest updates to Ghost, you can now replace the Ghost theme layer completely in favour of a front-end framework which builds your site statically from the Ghost API - effectively running Ghost as a Headless CMS.

Ghost ships with the Bookshelf.js ORM layer by default allowing for a range of databases to be used. Currently SQLite3 is the supported default in development while MySQL is recommended for production.

ghost architecture

The Art of the Possible....many possibilities

AWS is all about choice with over 175 fully fledged services, and in our case, too many choices is a good thing, as each service offers a different approach to run Ghost. I have categorised the 3 approaches, based on the relevant architectural patterns:

  1. Traditional: the dominant architecture pattern prior to cloud, this is typified by the 3-tier style. Even though it suffers from numerous challenges, most of us are comfortable with it, as it prevails as the dominant mental model when we architect new applications. Its characterised by overly separate and wasteful layers, an over-reliance on a relational (or even a single one-size-fits-all type of) database, and focusses only on the runtime and not on developer (DevOps) requirements (see this post I authored for the AWS Architecture Blog). With AWS, we can use single instances on Lightsail or EC2, or multiple instances behind a ALB Load Balancer and CDN, and single or clustered databases. Ghost can run like this, bit Ghost is’nt Wordpress, so it really deserves better.
  2. Modern: Cloud-native apps run on containers or Serverless as opposed to VMs/instances. They use DevOps to focus on how to push new features quicker and easier, and use (multiple) fit-for-purpose databases types. Using AWS, we can run docker as a container on Elastic Beanstalk, ECS, EKS, and developers/writers will use CI/CD tools like CodePipeline to automate pushing new posts to production. This is a better approach for running Ghost, as it takes advantage of Ghost’s lightweight nature, but still does not take full advantage of the headless capabilities of Ghost.
  3. Static: Perhaps specific to JAMstack-capable apps, this approach allows us to run Ghost as a headless and static site, with no dedicated compute layer. Using AWS, we can use Cloudfront as a CDN to serve content saved in S3.


I’ll try to cover the many different (but not complete or exhaustive) possibilities on how to run Ghost using the various AWS services. Instead of going into each in detail, I will highlight the benefit of each option, with pointers on how to build it.

Each option has some differences with respect to:

  • Storage: sqlite, MySQL, or even S3.
  • Editing and content creation: How to create and edit posts. Either directly in the Ghost admin, or done offline in git pushed, or pushed as a new docker image.
Option AWS Service/s Architectural Pattern Focus Time Range to build Cost p/m Ghost storage Ghost editing
Single instance on Lightsail Lightsail Traditional Ease of use and cost 1 min $3.50 Sqlite Directly in Ghost
Lightsail: multiple instances behind a LB, HA DB cluster, with a CDN Lightsail Traditional Ease of use, with scaling 5 mins $53.50 Sqlite or RDS Directly in Ghost
Markeplace image on EC2 EC2 Traditional Ease of use 1 min Sqlite Directly in Ghost
EC2: HA - multiple instances behind a LB, HA DB cluster, with a CDN EC2, Auto Scaling, RDS, ALB, CloudFront Traditional scaling, and full control 30 mins Sqlite, RDS, S3 Directly in Ghost, or offline
Container on EC2 EC2 Traditional Full Control 10 mins Sqlite, RDS, S3 Directly in Ghost, or offline
Elastic Beanstalk EB Traditional/Modern Ease of use, with scaling, full control 15 mins Sqlite or RDS Directly in Ghost, or offline
ECS/EKS EC2, Auto Scaling, RDS, ALB, CodePipeline, CodeBuild, CodeDeploy, ECR Modern Full control, CI/CD pipelines 60 mins Sqlite, RDS, S3 Directly in Ghost, or offline
S3 static hosting S3 JAMStack Full control, latency 60 mins
Amplify JAMStack
APIGW, Lambda, S3, CloudFront

Lightsail

Lightsail is an easy-to-use cloud platform that offers you everything needed to build an application or website, plus a cost-effective, monthly plan. Compared to the rest of AWS, Lightsail has a dramatically simplified console, and even though the machines run in EC2, you can't see them in the EC2 section of the AWS console. The instances run in a special VPC, but this aspect is also provisioned automatically, and invisible in the console. The point of Lightsail seems to be simplicity. The flexibility of EC2 (and much of AWS) leads inevitably to complexity. The target market for Lightsail appears to be those who "just want a simple VPS" without having to navigate the myriad options available in AWS services like EC2, EBS, VPC, and Route 53. There is virtually no learning curve.

In this case, Ghost will be configured by default to use sqlite, or you could installed MySQL on the same instance, or use the Lightsail database options either standalone or redundant. You will be using the Ghost Admin interface to create posts.

This AWS blog post provides details on how to run Ghost on Lightsail as two options: using blueprints, or as a docker container:

image.png

EC2

While not very different to the Lightsail options above, here we are using the native AWS console or APIs, giving you full control of all the resources. To run a single instance, you could select an image from the AWS Marketplace, which will provision it completely for you on EC2.
To run multiple instances, you will use Auto Scaling to control how many instances are running depending on load, and ALB to load balance traffic over those instances. You can include Cloudfront as a CDN.

In this case, Ghost will be configured by default to use sqlite, or you could installed MySQL on the same instance, or use the Lightsail database options either standalone or redundant.
If you choose to run Ghost in a container, upgrades are mostly pain-free, and you have the option to create content offline on your laptop, and push it via git.

Elastic Beanstalk

Elastic Beanstalk is an easy-to-use service for deploying and scaling web applications and services developed with Java, .NET, PHP, Node.js, Python, Ruby, Go, and Docker on familiar servers such as Apache, Nginx, Passenger, and IIS.
You can simply upload your code and Elastic Beanstalk automatically handles the deployment, from capacity provisioning, load balancing, auto-scaling to application health monitoring. At the same time, you retain full control over the AWS resources powering your application and can access the underlying resources at any time.

I’ve categorised Elastic Beanstalk under Traditional architectures, because Elastic Beanstalk will make it easy to build and deploy a typical 3-tier app. However, its also listed under Modern architectures, because it can help you run containers, as well as make it easier to deploy with a CI/CD pipeline.

You have 2 options here:

  • Elastic Beanstalk runs Node.js, so you can run Ghost natively. See this and this as well. In this case, Ghost is configured to use S3 as storage.
  • Run Ghost as a docker container, with the storage set to the default sqlite.

ECS/EKS

This detailed guide - Deploying Ghost to ECS using a CI/CD Pipeline - will help you build an AWS environment, using various AWS DevOps tools, that will allow you to push new Ghost content to a git repo (CodeCommit), which will kick off a pipeline (CodePipeline) to build (CodeBuild) the code in a container, store the image in a container repo (ECR), and do a blue/green deploy (CodeDeploy) of the new image to a container orchestration system (ECS), fronted behind a load balancer (ALB).

You can choose to use SQLite, RDS or S3 for storage, and you can create post offline and push them to Ghost in production. This is how modern apps should work.

S3 static hosting

This involves creating posts in Ghost running locally, then creating a static version of what Ghost is serving, and storing and serving it from S3. There are many ways to do it, using HTTrack or custom static generators, or Gatsby with a CodeBuild project to auto-deploy to S3

Amplify Hosted

AWS Amplify Console is a static web hosting service that accelerates your application release cycle by providing a simple CI/CD workflow for building and deploying static web applications. Simply connect your application's code repository in the console, and changes to your frontend and backend are deployed in a single workflow on every code commit. With AWS Amplify Console, you can deploy Single page apps (SPAs) built with frameworks like React, Angular, Vue, Ember; and static sites generated with frameworks like Gatsby, Eleventy, Hugo, VuePress, and Jekyll. You can also host simple static websites without connecting to a Git provider with a manual deploy by choosing to drag and drop a folder from your desktop, or reference an Amazon S3 bucket or external URL.

This article provides a way to Ghost with Amplify