How to deploy an Ember.js app to Netlify (milestone reached: site deployed)

Not long ago, I realized I'd reached the goals set to myself for when the app should be made publicly available. It was time to deploy it.

I'm going to explain the few things I needed to solve so that the app is fully functional on Netlify. If you're eager to check out the app, it now lives at https://www.sac-sac-mate.com.

Choosing a primary domain for your app

After you create a site on Netlify, you can choose a primary domain for it. Netlify recommends setting it to www.yourdomain.name (or another subdomain, like app) and not the root (or apex) domain, see their "To www or not to www" article.

The main reason is that if you have a CNAME record on a domain, you're not allowed to have any other DNS records on it, which would prevent you from using the domain for emails, for example.

So I set www.sac-sac-mate.com as my primary domain and added the bare domain, sac-sac-mate.com to be redirected to it:

I then set up a a couple of records at my DNS registrar (or, more precisely, the provider where I manage the DNS records) so that the site is also served from my custom domain:

ANAME	sac-sac-mate.com	elastic-fermat-51274b.netlify.com
CNAME	www.sac-sac-mate.com	elastic-fermat-51274b.netlify.com

The Netlify integration in my Github repository took care of the rest: when I next pushed to Github, my site was deployed at the custom domain.

Note that Netlify also adds an SSL cert via Let's Encrypt, requiring no action from you, which is quite something:

Oh, the pieces are missing.

The app worked, it showed the players, tournament and game results but when you wanted to replay a game, it turned out the pieces were missing. After some thinking I realized what was going on.

broccoli-asset-rev fingerprints the images when building for the production environment and replaces links with the fingerprinted version, but it doesn't do that for (partially?) static URLs in your application. I have this code for displaying a board square:

<div
  class="w-50px h-50px {{if @isLightSquare "light-square" "dark-square"}}"
  ...attributes
>
  {{#if @piece}}
    <img
      src="/images/pieces/wikipedia/{{@piece}}.png"
      alt={{@piece}}
    >
  {{/if}}
</div>

That wasn't fingerprinted but the actual images on disk were, which resulted in broken links.

The solution was to instruct broccoli-asset-rev to not fingerprint the piece images:

// ember-cli-build.js
module.exports = function(defaults) {
  let app = new EmberApp(defaults, {
    fingerprint: {
      exclude: ['pieces'],
    }
    // (...)
  });
};  

Oh, the app only loads at the top route

The pieces were there, but the app didn't load at any path except at https://www.sac-sac-mate.com. I couldn't share a link to https://www.sac-sac-mate.com/games/5, for example, which was a shame, because somebody (namely, Maxime Vachier-Lagrave) finally managed to defeat Carlsen (even though in a blitz game, but still).

Since there is no server-side generated page for other routes than the top-most one (where the index.html is served), Netlify shows a 404 for all paths other than /.

To have the Ember app's router take over and handle the URL path, you need to set up a rewrite for all paths. We want those paths to serve the index.html and have Ember's router handle the route.

The Netlify docs about Redirects and Rewrites were very clear about how to do that:

If you’re developing a single page app and want history pushstate to work so you get clean URLs, you’ll want to enable the following rewrite rule:
/*    /index.html   200

Instead of putting this configuration in _redirects and other Netlify config options in other files, I prefer having all Netlify config in one file, which seems a more consistent and future-proof solution. The file should be called `netlify.toml`:

# vendor/netlify.toml
[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

That's not sufficient, though. This file has to end up in the root of the build, which, in the case of an Ember app is the `dist` folder. After some research, I learned that the app.import method can add any file to the build, with a nice, simple API.

The destDir needs to be passed, as otherwise the destination folder is the same as the source directory (in this case, vendor).

// ember-cli-build.js
module.exports = function(defaults) {
  let app = new EmberApp(defaults, {
   // (...)
  });

  app.import('vendor/netlify.toml', {
    destDir: '/',
  });
  
  return app.toTree();
};

This did away with this second problem and I haven't come across other deployment-related issues so far.

An ode to Netlify

That was the first site/app I've deployed to Netlify and I couldn't be happier with the experience.

I got a lot of things without having to do anything: there's no explicit deploy step, the bare domain to www redirection works and my site is SSL-enabled (and the certificate is automatically refreshed). Most of the things work out-of-the-box and when I encountered a challenge, their docs really helped in solving the problem.

The UI is outstanding, things are easy to find and look and work great.

Give them a try the next time you're looking for an easy deployment solution.