How to Install Ghost On AWS Elastic Beanstalk


First, a brief background on what Amazon's Web Services Elastic Beanstalk is:

AWS 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, and Docker on familiar servers such as Apache HTTP Server, Apache Tomcat, Nginx, Passenger, and IIS 7.5/8. 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. There is no additional charge for Elastic Beanstalk - you pay only for the AWS resources needed to store and run your applications.

For more information on Elastic Beanstalk checkout the AWS documentation.

To get started you need to download the latest copy of Ghost and make a couple of small configuration changes before Ghost can be uploaded to AWS Elastic Beanstalk.

The configuration changes will include:

  • Change the connection port Ghost listens on
  • Change Ghost to use an AWS RDS instance for persistant data storage across redeployments
  • Change the Ghost Content directory to be stored in an AWS S3 bucket for persistant data storage across redeployments

Download and Configure Ghost

Run the following commands in your console application:

cd ~/Desktop/
curl -L -O https://ghost.org/zip/ghost-latest.zip
unzip -d ghost ghost-latest.zip
cd ghost
cp config.example.js config.js
vim config.js

Replace the production section with the following:

    production: {
        url: process.env.URL,
        mail: {},
        database: {
             client: 'mysql',
             connection: {
                 host: process.env.RDS_HOSTNAME,
                 user: process.env.RDS_USERNAME,
                 password: process.env.RDS_PASSWORD,
                 database: process.env.RDS_DB_NAME,
                 port: process.env.RDS_PORT
         },
             debug: false
         },
        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '0.0.0.0',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: process.env.PORT
        }
    },

Each of the process.env are environmental variables that AWS Elastic Beanstalk sets for us, except for process.env.URL which we will be setting.

To make Ghost start up in production mode and set the process.env.URL we need to create an Elastic Beanstalk configuration file to create environment variables:

mkdir .ebextensions
vim .ebextensions/environment.config
option_settings:
  - option_name: NODE_ENV
    value: production
  - option_name: URL
    value: http://ghost-env.elasticbeanstalk.com

For testing purposes I have set my URL variable to the hostname that Elastic Beanstalk provided me when I deployed. You will likely want to set this to your personal domain.

Configure Ghost to Store Content Directory in S3

You will need to create two more Elastic Beanstalk configuration files to download and install S3fs and mount your S3 bucket in place of the Content directory. In this first file you do not need to make any changes.

vim .ebextensions/98-install_s3fs.config
packages:
  yum:
    kernel-devel: []
    pkgconfig: []
    automake: []
    gcc: []
    git: []
    openssl-devel: []
    libstdc++-devel: []
    gcc-c++: []
    fuse: []
    fuse-devel: []
    libcurl: []
    libcurl-devel: []
    libxml2-devel: []
    openssl-devel: []
    mailcap: []
sources:
  /home/ec2-user/: http://github.com/s3fs-fuse/s3fs-fuse/archive/v1.77.tar.gz
files:
  "/etc/fuse.conf" :
    owner: root
    group: root
    content: |
      # mount_max = 1000
      # user_allow_other
commands:
  01-autogen:
    command: ./autogen.sh
    cwd: /home/ec2-user/s3fs-fuse-1.77
  02-configure_s3fs:
    command: ./configure --prefix=/usr
    cwd: /home/ec2-user/s3fs-fuse-1.77
  03-make_s3fs:
    command: make
    cwd: /home/ec2-user/s3fs-fuse-1.77
  04-install_s3fs:
    command: make install
    cwd: /home/ec2-user/s3fs-fuse-1.77

In this second file you will need to make a couple of small changes.

  • AK...L2A:fb...rE needs to change to your AWS API Key and Secret in the form of APIKEY:SECRET
  • Replace YOUR-S3-BUCKET with the name of your S3 bucket
vim .ebextensions/99-mount-s3.config
files:
  "/etc/passwd-s3fs":
    mode: "000640"
    owner: root
    group: root
    content: |
      AK...L2A:fb...rE
  "/opt/elasticbeanstalk/hooks/appdeploy/pre/1_unmount_s3_bucket.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash

      # Umount /var/app/current/content if it is mounted so that application can be redeployed

      if mount | grep s3fs > /dev/null; then
        echo "The s3fs mount was found...umouting it..." >> /var/log/ghost_deploy.log
        umount s3fs
      else
        echo "The s3fs mount was NOT found!" >> /var/log/ghost_deploy.log
      fi
  "/opt/elasticbeanstalk/hooks/appdeploy/post/99_restart_delayed_job.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash

      service nginx stop
      initctl stop nodejs

      # NOTE: In a pre hook if an S3 bucket is mounted it will be unmounted so that it can be mounted after the app has been deployed

      mv /var/app/current/content /var/app/current/content_tmp
      mkdir /var/app/current/content
      /usr/bin/s3fs YOUR-S3-BUCKET /var/app/current/content -o allow_other -o use_cache=/tmp
      sleep 5

      # Check for lock file to determine if this is first deployment
      if [ -f /home/ec2-user/.initial_ghost_deploy ]
        then
        echo "The lock file exists so this is a redeployment.  Do NOT copy default /content directory to S3 bucket." >> /var/log/ghost_deploy.log
      else
        echo "The lock file does not exists so this is the first deployment" >> /var/log/ghost_deploy.log
        cp -r /var/app/current/content_tmp/* /var/app/current/content/
        chown -R nodejs:nodejs /var/app/current/content/*
      fi

      rm -r /var/app/current/content_tmp

      # Create a lock file so that we know if this is the first deployment
      touch /home/ec2-user/.initial_ghost_deploy

      service nginx start
      initctl start nodejs

Now that all Ghost configuration changes are complete we need to zip up Ghost for deployment to AWS Elastic Beanstalk:

zip -r ghost-elasticbeanstalk.zip .

With this last command complete you will end up with a new zip called ghost-elasticbeanstalk.zip located at ~/Desktop/ghost/ghost-elasticbeanstalk.zip. This zip will be uploaded to AWS to create our Ghost instance.

Create AWS Elastic Beanstalk Ghost Instance

If you do not already have an AWS account create one first.

  1. To get started with AWS Elastic Beanstalk first get logged into your AWS account.
  2. Next open up the Elastic Beanstalk section. Be sure to select your desired AWS region.
  3. Fill in a name and description for your AWS Elastic Beanstalk application and click Next
  4. alt
  5. On the Environment Type page select the following and click Next
    • Environment tier = Web Server
    • Predefined configuration = Node.js with 64bit Amazon Linux 2014.03 v1.04 (or whatever the latest 64bit version is)
    • Environment type = Single instance
    alt
  6. On the Application Version step use the "Upload your own" option and select the ghost-elasticbeanstalk.zip we created earlier.
  7. alt
  8. On the Environment Information page fill out the fields like the following
    • Environment name = Typically the environment name would be "development" or "production" but it can be anything you would like. Check out the AWS Elastic Beanstalk FAQ for more details on what the environments features provides.
    • Environment URL = Your desired URL for this Ghost environment
    • Description = Whatever description you would like for this Ghost environment
    alt
  9. On the Additional Resources page check the "Create an RDS DB Instance with this environment" checkbox
  10. On the Configuration Details page select the following
    • Instance type = Your desired Amazon EC2 instance size. Check the AWS documentation for spec and pricing details
    • EC2 key pair = Select your SSH public key which will be added to the EC2 instance
    • Email address = Add your email address for alerts
    • Instance profile = Leave this is the default
    • Application health check URL = Leave empty
    • Enable rolling update = Leave unchecked
    alt
  11. On the Environment Tags page click Next
  12. On the RDS Configuration page select the following options
    • Snapshot = None
    • DB engine = mysql
    • Instance class = Your desired Amazon RDS instance size. Check the AWS documentation for spec and pricing details (The micro will work for most people)
    • Allocated storage = The 5GB minimum is a great place to start. Additional storage can be added later.
    • Username = Your desired database username
    • Password = Your desired database password
    • Retention setting = Create snapshot
    • Availability = Single Availability Zone
    alt
  13. On the Review page click Launch
  14. Your Elastic Beanstalk environment will take a few minutes to create. You will know your environment is setup when your Health turns to Green.

A complete zip with Ghost 0.5.0 and the required configuartion files can be downloaded here. The only changes you will need to make are adding your AWS API Key, secret, and S3 bucket.

Notes:

  • Elastic Beanstalk in the background is just creating EC2 instances. You have full control of these instances. If you need to make a change you can SSH into the EC2 instance.
  • Ghost gets installed into /var/app/current/
  • The AMI that Amazon uses for running Node.js Elastic Beanstalk applications has Nginx installed and listening on port 80. Requests are proxied to port 8081 by Nginx.