You have your Ember web app. You know you want to use CloudFront for your CDN and S3 for storage. But how do you put all the pieces together? This article from our DevOps experts guides you through all the code and commands necessary for configuration and deployment.

When your web application or website goes live you want it to perform well, load quickly and operate in the manner expected by your visitors. This can all be possible with the combination of Ember, Amazon S3, and the CloudFront CDN.

In this post we will show you how you how to host an Ember application on Amazon S3 with Amazon’s CloudFront CDN in front of it, as part of your DevOps configuration. Ember is a JavaScript framework that’s used in projects worldwide and as a fully featured framework contains all the elements necessary for developers to ship new features quickly. Amazon S3 is Amazon’s cloud storage and their CloudFront CDN allows you to distribute your web app over servers around the world to give your visitors the speediest, smoothest web experience available. You can do this by choosing server locations closest to your visitors - which we set up in this tutorial. For the speediest serving, all static assets(JavaScript, CSS, image files) should be cached (by using long TTL) so on their next visit these elements will be served directly from the disk.

In short, this Ember.js hosting configuration will allow you to serve your Ember-crafted content in a fast, secure manner worldwide.

Note: For the purposes of this article, we are going to assume that your app uses ember-cli.

Let’s get started using CDN with Ember.js!

Application configuration

You need to install ember-cli-deploy with a few additional add-ons for deploying to S3:

ember install ember-cli-deploy
ember install ember-cli-deploy-build
ember install ember-cli-deploy-display-revisions
ember install ember-cli-deploy-revision-data
ember install ember-cli-deploy-s3
ember install ember-cli-deploy-s3-index
ember install ember-cli-deploy-manifest
ember install ember-cli-deploy-gzip

As a bit of a background, ember-cli-deploy is base skeleton to customize deployment, with additional add-ons like build (to build the app), s3 (to push app to S3) and s3-index (to create versionized index.html files).

display-revisions and revision-data are required dependencies for s3-index. ember-cli-deploy-manifest will allow us to upload only those files that have changed since our last deployment (differential upload) and ember-cli-deploy-gzip will make sure our assets are compressed.

Now we need to the create configuration file for deployment:

// config/deploy.js
module.exports = function(deployTarget) {
  var ENV = {
    pipeline: {
      activateOnDeploy: true
    }
  };
 
  ENV.cloudfront = {
    accessKeyId: 'AWS_ACCESS_KEY',
    secretAccessKey: 'AWS_SECRET_KEY',
    distribution: 'CLOUDFRONT_DISTRIBUTION_ID'
  };
 
  ENV['s3'] = {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    bucket: 'my-ember-app',
    region: 'us-east-1'
  };
 
  ENV['s3-index'] = {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    bucket: 'my-ember-app',
    region: 'us-east-1'
  };
  return ENV;
};

Note: Even if the settings for s3 and s3-index are the same, do not assign them to the same JavaScript object - it will produce an error which will be difficult to trace (GitHub issue).

If you need to support additional environments (like staging) add them to the config/deploy.js file as well:

if (deployTarget === 'staging') {
  ENV.build.environment = 'staging';
  ENV.s3.bucket = ENV['s3-index'].bucket = 'my-ember-staging-app';
  ENV.s3.region = ENV['s3-index'].region = 'us-east-2';
  ENV.cloudfront.distribution = 'CLOUDFRONT_STAGING_DISTRIBUTION_ID';
}
 
if (deployTarget === 'production') {
  ENV.build.environment = 'production';
  ENV.s3.bucket = ENV['s3-index'].bucket = 'my-ember-prod-app';
  ENV.s3.region = ENV['s3-index'].region = 'us-east-1';
  ENV.cloudfront.distribution = 'CLOUDFRONT_PRODUCTION_DISTRIBUTION_ID';
}

Now if you run ember deploy {production|staging}, your app will be built and pushed to your S3 bucket.

S3 bucket configuration

Add the following policy and CORS configuration for your bucket:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-ember-app/*"
        }
    ]
}
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>http://www.my-ember-app.com</AllowedOrigin>
        <AllowedOrigin>https://www.my-ember-app.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>HEAD</AllowedMethod>
    </CORSRule>
</CORSConfiguration>

Remember to replace http://www.my-ember-app.com and https://www.my-ember-app.com with your own domain.

Also turn on Static Website Hosting and provide index.html as Index Document.

Using history location

If your app uses history-api for nice urls, you will need additional configuration for your S3 bucket:

  1. Set index.html as Error Document in Static Website Hosting
  2. Add the following Redirection Rules:
<RoutingRules>
    <RoutingRule>
        <Condition>
            <HttpErrorCodeReturnedEquals>403</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <HostName>www.my-ember-app.com</HostName>
            <ReplaceKeyPrefixWith>#/</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
</RoutingRules>

The above redirect rules are not necessary if you are going to use the app via CloudFront only. However for some environments i.e. preview, you might not need CloudFront and access via direct a S3 link is more than enough.

Deploy user configuration

Below you can find a minimal policy that will allow your AWS user to deploy files to the previously created S3 bucket:

{
    "Statement": [
        {
            "Action": [
                "s3:ListAllMyBuckets",
                "s3:GetBucketLocation"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectACL"
            ],
            "Resource": [
                "arn:aws:s3:::my-ember-app/*"
            ]
        }
    ]
}

Ember.js on CloudFront configuration

  1. Create a CloudFront distribution with your bucket s3 endpoint as an origin and index.html as Default Root Object. It’s very important to use the endpoint instead of the bucket name i.e. your-bucket-name.s3-website-us-west-2.amazonaws.com - otherwise you can receive 403 errors when you try reload the page.
  2. In the Alternate Domain Names (CNAMEs) field, provide the domain you will use for your app.
  3. Create a Custom Error Response in your Error Pages (inside Distribution ID settings):
    1. HTTP Error Code: 404: Not Found
    2. Customized Error Response: Yes
    3. Response Page Path: /index.html
    4. HTTP Response Code: 200: OK
  4. Point your subdomain to Domain Name value from the CloudFront page (as CNAME)

Now your configuration should be ready and the app will respond under your domain name after deployment.

Deployment

To deploy your Ember app on S3 simply run the ember deploy production command.

Other useful commands

Here are a few extra commands that you may find useful:

// Deploy
ember deploy staging
ember deploy production
 
// List available revisions
ember deploy:list staging
ember deploy:list production
 
// Activate revision manually
ember deploy:activate staging --revision xyz
ember deploy:activate production --revision xyz

If you would like to see more info about the deployment process pass the --verbose flag to the command, i.e. ember deploy staging --verbose.

We help companies all over the world with managing and deploying their web applications in the most efficient and effective ways - utilizing the best in breed cloud services, including Amazon’s suite of platforms. We also offer custom web application in Ember and JavaScript, along with a host of other technologies. Could your business benefit from more stable, dynamic software or more frictionless infrastructure? Contact our team to find out how iRonin can help to supercharge your ongoing and new projects.