Deploying Ghost to AWS with ECS

I'm a big fan of Ghost. It's everything I'm looking for in a blogging platform - simple, extensible, and very  developer-friendly. My previous website (www.ryandao.net) ran on Ghost as well. I had it deployed to a single EC2 micro instance using a deployment strategy I cooked up myself. It basically used a Git post-commit hook to push the code to the instance and run npm commands to start up the service. The service was kept running using some nanny scripts, ensuring the website stayed up even if the instance was unstable. I had it running for a couple of years without any issue, until the day npm refused to cooperate.
You see, I'm not someone who likes to regularly update things if they're not broken. I ran an early version of Ghost (0.11.x) throughout the entire lifespan of the site. However, it reached a point when my Ghost version was too far behind that some of its npm dependencies were either recalled or simply couldn't work with each other. When the EC2 instance was rebooted (which happened not so infrequently for micro instances), npm install stopped working. Updating to the latest version of Ghost (3.x) was the only sensible option to get the site running again, but it was quite a bit of work. Since the contents on the blog were also outdated, I decided to let it die and create a new one.
For this new blog, I still want to stick with Ghost, but I need a more reliable deployment strategy. The site needs to be as cheap as before (roughly $6/month) to justify managing my own infrastructure but more available and easier to upgrade. That brings be to Amazon Elastic Container Service (ECS).

Quick ECS overview

ECS is a managed container orchestrator service for deploying Docker containers to EC2 without having to manage the underlying infrastructure. The service comes with 2 deployment options - EC2 and Fargate. The EC2 option allows you to use an autoscaling group (ASG) as the capacity provider and ECS will take care of managing the containers running on the ASG instances. Fargate is a serverless option that dynamically places your containers in a pool of instances managed by the service. Fargate is generally more expensive and more suitable for on-demand workloads like data analytics or cron-job-like tasks. For a Ghost website that needs to be up all the time, the EC2 deployment option is much cheaper. By using ECS on EC2, you have the benefits of managed deployment and maintenance without any additional cost on top of the underlying infrastructure.

Deploying Ghost to ECS

ECS supports multi-instance deployment using an Elastic Load Balancer (ELB), but it's not necessary the blog contents are highly cachable. Using a single EC2 instance with a CDN in front can support the same scale at a fraction of the cost. Here's a high-level overview of how to set this up:

  1. Create an ASG with the desired capacity of 1.
  2. Create an ECS cluster with the ASG as the capacity provider.
  3. Create an Elastic File System (EFS) to use as the permanent storage layer. This is important since Docker containers are stateless. Without a permanent storage layer, all your data will be lost when ECS stops and starts the container.
  4. Create an ECS task definition to configure the Docker image. We can use the community image on Dockerhub.
  5. Use the task definition to create and run an ECS service in the cluster.
  6. Create a CloudFront distribution with the ECS service as the origin. The IP address of the service is the IP address of the EC2 instance hosting the service. If you want to use HTTPS (highly recommended), you can assign an ACM certificate to the CloudFront distribution.
  7. (Optional) Use a Lambda function to monitor the website's uptime. It runs every couple of minutes checking if the site is accessible. The function will trigger an email notification via Amazon Simple Notification Service (SNS).


I use AWS Cloud Development Kit (CDK) to set all of this up. CDK is an infrastructure-as-code framework on top of CloudFormation, allowing developers to automate infrastructure creation and maintenance using various programming languages. My Python CDK project for the website is available here https://github.com/ryandao/ghost-cdk. Feel free to follow the instructions in the README if you want to do the same.

Cost estimation

The biggest advantage of owning my own infrastructure is cost. A lowest-tier managed Ghost hosting solution is $20/month at the point of writing. This setup costs me roughly $6/month. Here's the cost breakdown:
EC2: I use a t2.micro reserved instance which costs $5/month.
ECS: Free
CloudFront: Free up to 2M requests per month
EFS: $0.03
Lambda: Free up to 1M requests per month
ACM: Free
Route53: $0.5 for a hosted zone managing the website's DNS records.