Ghost on Elastic Beanstalk pt. 1

EDIT: August 2, 2019: Ghost has upgraded to 2.0 so these instructions may not be accurate anymore. Some parts of this may still be helpful.

EDIT: March 10, 2016: This is part one of a two part post on deploying Ghost to Elastic Beanstalk end to end.

Part One - Getting Ghost Production Ready

As mentioned in my previous post, I am using the Ghost publishing platform for this blog. For the Wordpress aficionados, it is very similar. I will not get into the details of the differences or capabilities of this platform. Instead, I will discuss the intricacies, tips, and summary of how I got this thing live, in particular, how I got this live in AWS Elastic Beanstalk.

Wait, but there are already a bunch of articles out there

Yeah you're right, there are several articles out there that already touch on this topic. But, as I was going through them several months back, I noticed that some were outdated and most were half-complete. I will try my best to give you the end-to-end steps I took to get this thing live. If applicable, I will link the appropriate instructions for certain steps. Some existing Ghost deployment articles copy and paste the old instructions, so I will provide the links to make sure you always have the latest docs to reference. I will also go over my continuous deployment pipeline with Codeship to make deployments super stress free. I plan to keep this post updated regularly with any new changes if people run into issues. Now lets get started!

Keys to Success (ha!)

Here is the run down of what we're going to do.

Part One

  1. Download Ghost and get it running
  2. Update your Ghost config
  3. Create multiple IAM users
  4. Create an S3 bucket
  5. Initialize your Elastic Beanstalk Application
  6. Deploy that shiet
  7. Configure Elastic Beanstalk

Part Two

  1. Associate a domain name
  2. Integrate Mailgun
  3. Enable HTTPS
  4. Set up your continuous deployment pipeline
  5. Integrate a theme

Take some notes

As we're going through steps 1-5, we're going to need to jot down some notes. There are going to be some variables you'll need to keep track of as we move along, so here's the list of variables:

  • IAM_ACCESS_KEY
  • IAM_SECRET_KEY
  • NODE_ENV
  • RDS_DB_NAME
  • RDS_HOSTNAME
  • RDS_PORT
  • RDS_USERNAME
  • RDS_PASSWORD
  • S3_BUCKET_NAME
  • S3_BUCKET_REGION
  • SMTP_SERVICENAME
  • SMTP_USERNAME
  • SMTP_PASSWORD
  • URL

To start off, you assign the value production to the NODE_ENV variable.

Download Ghost and get it running

Simply download the latest release here and follow the instructions to get it running locally on your machine.

Note: You may also be tempted to download their latest on Github directly. I would highly advice against this, because the release version is already configured and bundled to be production ready.

Update your Ghost config

You'll have to update your Ghost config to get it ready for production.

You're going to first want to install ghost-s3. This will enable us to use S3 to serve our static assets. Please follow the instructions on the github repo to set it up.

Then, you're going to want to copy and paste the following config into the production property.

 production: {
        url: process.env.URL,
        mail: {
            transport: 'SMTP',
            options: {
                service: process.env.SMTP_SERVICENAME,
                auth: {
                    user: process.env.SMTP_USERNAME,
                    pass: process.env.SMTP_PASSWORD
                }
            }
        },
        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: '0.0.0.0',
            port: process.env.PORT
        },
        storage: {
            active: 'ghost-s3',
            'ghost-s3': {
                accessKeyId: process.env.IAM_ACCESS_KEY,
                secretAccessKey: process.env.IAM_SECRET_KEY,
                bucket: process.env.S3_BUCKET_NAME,
                region: process.env.S3_BUCKET_REGION
            }
        }
    },

I will go over what all of these mean later, you might also notice that the variables I told you to jot down earlier are used here.

Before finishing off here and going into the DevOps work, please run the following command

npm shrinkwrap

The Ghost installation that you downloaded earlier should have already had this set up. However, we'll have to run it again in order to account for the new ghost-s3 package dependency.

Create multiple IAM users

You're now going to want to create multiple IAM users to create accounts with different permission levels of your environment. We will use these accounts to manage our deployment pipeline and S3 access.

Assuming you've already created an AWS account, the easiest way to create your IAM users is to go through the AWS management console. Follow the instructions here on how to get that done.

You'll want to create two users, one for your Ghost blog and one for Codeship, so create a ghost user and codeship user. Your Ghost blog will need an IAM user to access your S3 bucket, which we'll create in a future step. But for now, you'll want to attach an S3 policy to your ghost user and the S3 and ElasticBeanstalk policies to your codeship user. Read more on how to attach policies here.

When you create your users, you'll be taken to page that will display your user's Access Key Id and Access Key Secret. Make sure to save those in a secure location and DO NOT upload them anywhere. Remember how I told you that you'll need to jot down some notes? Well, here's your first set up values to set. When you create your ghost user, you're to want to associate set IAM_ACCESS_KEY to the Access Key Id, and the IAM_SECRET_KEY to the Access Key Secret.

Create an S3 bucket

You're now going to want to set up an s3 bucket. Because you're creating a blog, you'll have a lot of static assets that you'll probably be using and uploading. Ghost automatically manages these files within its local file system. You can potentially see why that won't scale well. So we're going to leverage Amazon S3 to for simple cloud storage.

ON the AWS management console, go to the S3 service and click the Create Bucket button on the top. Make sure to select the same region as your Elastic Beanstalk deployment. Once you create your bucket, you'll want to configure its permissions to give public read access to your files.

Click on the created bucket, click edit bucket policy and paste the code below.

{
	"Version": "2016-03-01",
	"Statement": [
		{
			"Sid": "AllowPublicRead",
			"Effect": "Allow",
			"Principal": {
				"AWS": "*"
			},
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::exampleBucketName/*
		}
	]
}

IMPORTANT: Make sure to update the exampleBucketName in the config above with the name of the bucket you just created.

So, if your bucket name is ghost-blog-bucket, then it will look like

arn:aws:s3:::ghost-blog-bucket/*

Jot down both the bucket name and bucket region for the S3_BUCKET_NAME and S3_BUCKET_REGION variables.

Initialize your Elastic Beanstalk Application

You can create a new Elastic Beanstalk application through the AWS console. However, I prefer you do it through the CLI (for reasons explained later).

Fire up a terminal and navigate to the directory of your Ghost installation. If you haven't already, install the AWS elastic beanstalk cli as well. Assuming you're using a MAC, the best way to do this is to download it through Homebrew. Run the following command to do this (also documented here):

brew install awsebcli

Once you do that, initialize a new application by following the instructions here.

IMPORTANT: Remember to name your application meaningfully and select a region.

Deploy that Shiet, create your Elastic Beanstalk Environment

I do not recommend creating your Elastic Beanstalk environment through the AWS console. This is mainly because you cannot auto create a MYSQL RDS instance, this is broken on the Elastic Beanstalk console when I first deployed (01-24-2016). So, you're going to want to create your environment through the CLI.

IMPORTANT: Make sure your current working directory is at the root of your Ghost installation.

Run the following command to create your environment.

eb create --database

Follow the prompts, and remember to name everything properly. You can read more about the possible arguments here. Because you executed this command in the proper context/directory, it will automatically recognize you're building a Nodejs environment.

Be sure to take notes on your database name, hostname, password, port, and username, because you will be using those to fill in RDS_DB_NAME, RDS_HOSTNAME, RDS_PASSWORD, RDS_PORT, and RDS_USERNAME respectively. You can also see most of these values again in the Elastic Beanstalk console.

Configure Elastic Beanstalk

Please check your Elastic Beanstalk management console to see when you're deployment is complete. We're almost live.

We will now need to do the first set up configuration setup on the environment. We will do one more round in part 2, but this will the minimal amount needed to get your app to work.

Remember all those notes I asked you to write earlier? They will finally come in handle. On your new Elastic Beanstalk environment, navigate to Configuration and you'll want to go into Software Configuration.

Make sure your Node version is at least 4.2.3. Then, on the bottom of the page, you'll want to add your environment variables. As a refresher, here are all the variables you'll need to add.

  • IAM_ACCESS_KEY
  • IAM_SECRET_KEY
  • NODE_ENV
  • RDS_DB_NAME
  • RDS_HOSTNAME
  • RDS_PORT
  • RDS_USERNAME
  • RDS_PASSWORD
  • S3_BUCKET_NAME
  • S3_BUCKET_REGION
  • SMTP_SERVICENAME
  • SMTP_USERNAME
  • SMTP_PASSWORD
  • URL

Populate each field with exactly what I told you to take notes on earlier. You can populate the URL with the Elastic Beanstalk generated URL. At this point, you'll still be missing the SMTP variables, we'll fill them in on part 2 of this post. For now, leave them blank.

In our configuration setup in step 2, we are already referencing all of these variables that we just set up. This is done to ensure that all of our keys are not exposed haphazardly.

After you click Save, your app will redeploy, and hopefully you should be live!

Congrats! You just finished part one!

Phew! That was a lot to do! You should now have a functional Ghost blog out in production. Part two will focus on the refinement of our blog and getting it ready for users.