AWS DevOps Blog

Six Steps to Deploy Ghost to AWS Elastic Beanstalk

Today’s post comes from Sebastien, Technical Trainer for AWS. Based in Luxembourg, he helps our customers and partners gain proficiency with AWS services and solutions. You can follow him on Twitter @sebsto.

Ghost is a new and popular blogging platform based on Node.js. An open source application, Ghost aims to bring fun and creativity back to blogging. As the developers at Ghost.org put it, Ghost is “simple, elegant, and designed so that you can spend less time making your blog work and more time blogging.”
 
We like the idea of giving Ghost’s users more time to blog by minimizing time spent managing the underlying infrastructure. It’s the same approach that AWS Elastic Beanstalk uses to help you quickly deploy and manage applications in the AWS cloud without worrying about the infrastructure that runs those applications.
 
So in that spirit, let’s look at how to deploy Ghost to AWS Elastic Beanstalk. 
 
Unfortunately, the current version of Ghost (0.4.1 at the time of this writing) cannot be deployed “as is” to Elastic Beanstalk for several reasons:
  • The default Elastic Beanstalk’s web container (Nginx) forwards HTTP requests to Node.js on port 8081.  Ghost is configured to listen on port 2368 by default.
  • Elastic Beanstalk’s default Node.js version is 0.8.26 while Ghost expects 0.10.21.
  • Ghost’s Node.js SQLite module is compiled against GLIBC 2.14, which is not available on Amazon Linux.
The good news is that by customizing a few configuration files, you can deploy Ghost to Elastic Beanstalk in just six quick steps.

Step 1: Get the Ghost Distribution

Download and unzip ghost’s distribution from the Ghost download page.
 

Step 2: Change the default Node.js HTTP port

In the extracted distribution files, locate config.example.js and open it in your preferred editor.  Change server’s port number from 2368 to 8081, the port used by Elastic Beanstalk’s HTTP container (Nginx).
server: {
  // Host to be passed to node's `net.Server#listen()`
  host: '127.0.0.1',
  // Port to be passed to node's `net.Server#listen()`,
  // for iisnode set this to `process.env.PORT`
  port: '2368'
}

Do this for both the “Development” and “Production” environments, which are two different sections in the configuration file.

Step 3: Change the Node.js version

Next, you need to instruct Elastic Beanstalk to use Node.js 0.10.21 instead of the default version. To do that, create an .ebextensions directory in your ghost directory and a 00_ghost.config file in that directory.  The 00_ghost.config file must contains the following:
option_settings:
  - namespace: aws:elasticbeanstalk:container:nodejs
    option_name: NodeVersion
    value: 0.10.21

This will ensure Elastic Beanstalk will use Node.js 0.10.21 to host Ghost.

The Node.js package installer is able to work around the absence of GLIBC 2.14 to automatically recompile the SQLite module from the sources, linking it to the provided GLIBC library. Consequently, we need to do the following:

  • Remove Node.js SQLite dependency to ensure Node.js will not try to automatically install the binary module, which is linked to the missing library.
  • Install the required compiler packages so that the Node.js package installer can compile the module from the source.
  • Instruct the Node.js package installer to install Node.js SQLite module (which Ghost dependencies require)

Let’s go through each of these steps.

Step 4: Remove the SQLite Dependency

Remove “sqlite3” dependency from package.json file by deleting its line from the file.
 

Step 5: Install GCC and GCC-++ Packages

Edit our configuration file .ebextensions/00_ghost.config, adding the following content:
packages:
   yum:
      gcc: []
      gcc-c++: []

This will instruct Elastic Beanstalk to install the gcc and gcc-c++ packages from the yum repository before deploying the application.

Step 6: Install Node.js SQLite Module

Finally, install the Node.js SQLite module from the sources. We removed the dependency during step 4, so we need to make sure the module will be available to Ghost.

Installing from source might sound scary and/or complicated, but the Node.js package installer makes it very easy. We just need to have one command executed before the deployment of the application.

Elastic Beanstalk provides hooks to run scripts before and after application deployment. So all we need to do is add the required command to our configuration.

Edit .ebextensions/00_ghost.config created earlier and add the following:

container_commands:
   10_pre_install_sqlite3:
      command: "/opt/elasticbeanstalk/node-install/node-v0.10.21-linux-x64/bin/npm install sqlite3@2.1.16 --build-from-source"
      cwd: "/tmp/deployment/application"
      env:
          HOME: "/root"
          PATH: "/sbin:/bin:/usr/sbin:/usr/bin:/opt/aws/bin"
Let’s walk through these lines:
  • container_commands: is the group of commands Elastic Beanstalk will execute before application deployment.
  • command: is the actual command that installs the module and compiles it from the source.
  • cwd: is the current path where the command will be executed.
  • During compilation, the gcc compiler expects some environment variables to be available: HOME and PATH. So be sure to include these as well.
Et voilà.
 
With just this one configuration file, we configured our Elastic Beanstalk environment to use a specific Node.js version, install a couple of Linux packages, and install a Ghost dependency from the sources.
 
Now you are ready to deploy Ghost.  Just zip your directory and deploy the ZIP file from the Elastic Beanstalk console.  Be sure the zip file contains the .ebextensions folder. One way to do this is to type the following command at a command prompt from the Ghost folder:  zip –r ../ghost.zip * .*
 
If you prefer to deploy from the command line,  go through the usual eb init, eb start steps.

Persisting Data

In a future post, we’ll cover replacing SQLite with a persistent relation database, and look at options for storing static content generated by Ghost in a durable location.

As usual, please use the Comments section to give us your feedback.