Jekyll + Firebase Hosting

Jekyll + Firebase Hosting

This site is built with Jekyll and was hosted on GitHub Pages until recently, with all of the commit-to-deploy goodness that it gives you for free. I found that hosting on GitHub does have some downsides though:

  • SSL does not work with custom domains (CNAME) unless you use a proxy (such as CloudFlare).
  • You need a paid account to have private repositories.
  • There’s limited Jekyll plugin support. You only have access to what they whitelist.

Over the past few weeks I’ve been going through the process of moving this site away from GitHub Pages, mostly as a technical exercise.

Hosting

I decided to host the site on Firebase Hosting. Taking off my Google employee hat, the hosting functionality is one of my favourite bits of Firebase because of:

  1. Working custom domain + SSL.
  2. Custom rules (for rewriting of headers, responses, redirects, etc).
  3. Automatic free worldwide CDN edge servers.

I was previously using CloudFlare in front of GitHub Pages to workaround these points. CloudFlare is awesome, but it limits the amount of page rules (3) you can have on the free tier and I needed more, plus removing one more service from the mix is good.

Source hosting (optional)

To make my site repository private, I either had to pay GitHub $7 per month or move it somewhere else. I have no issue with paying for services I use, but paying $7 per month just to host a private repo seemed a bit silly to me. Instead I moved my repository to Bitbucket which gives you 1GB of storage space for free. It also has all the features I use from GitHub, so migrating was easy. Simply create the repository and then:

# Remove GitHub remote
git remote remove origin

# Add Bitbucket remote
git remote add origin git@bitbucket.org:username/repo-name.git

# Push source to Bitbucket
git push origin master

Commit-to-deploy

By moving away from GitHub Pages, the automatic commit-to-deploy was lost, which made blogging on Jekyll nice. I can however make use of continuous integration services to build and deploy the site automatically. I decided on using CircleCI since:

  • I’ve used it before and know how to write circle.yml files.
  • It integrates with Bitbucket and GitHub nicely.
  • It’s free for up to 1,500 build hours per month, which is plenty. My site build takes less than 3 minutes so I have headroom for over 500 builds per month.

There are others CI services out there which offers a similar proposition, such as Travis CI or Codeship.

Setup

So you now know my rationale for moving and how I planned to do it, I’ll talk about how you can setup the same for your site.

I assume from this point that you have a working Jekyll site and are using bundle, if not I’d recommend that you go through the Jekyll Quick-start guide first. I also assume that you have a working git repository on either Bitbucket or GitHub.

Firebase Hosting

First step, you need to create a project on the Firebase Console to host your files. You can call it whatever you want. Once created, you have your hosting project set-up. Easy eh?

To actually start pushing files to the hosting, you need to use the Firebase CLI tool. Before you can use that though, a bit of machine setup is necessary.

Machine setup

The Firebase CLI tool requires Node to be installed (instructions here). Once Node is installed, you can install the Firebase CLI tools by running npm install -g firebase-tools. Once that is done you need to give the tool access to your account, by running firebase login in your terminal, and following the steps through to completion.

Configuration

The next step is to configure your project with Firebase. Switch to your terminal, change directory to your site’s root and run firebase init. Select ‘Hosting’, and then select your project. You will now have two new files in your project: firebase.json and .firebaserc.

You can ignore .firebaserc since that just tells the tool which Firebase project you’re using. firebase.json is where you can configure your hosting, and you need to make a change to it to make it work with Jekyll.

By default, the firebase tool expects the folder it deploys (uploads) to be called public whereas Jekyll (by default) builds to _site, therefore you need to tell it so. Open up firebase.json in your text editor of choice and you should see an empty file. Paste in the following and save it:

{
  "hosting": {
    "public": "_site",
    "ignore": [
      "**/.*",
      "firebase.json"
    ],
  }
}

As you can probably guess, the public key tells the tool which folder to deploy and above is just setting it to use _site instead. The ignore key is optional, but tells the tools to ignore certain files.

After this is done you’re ready to try and deploy your site for the first time. Run firebase deploy --verbose and wait for your site to be deployed. After it has finished you can use your web browser of choice to see your new site in action! 🙌

Configure Circle CI

So you now have a site which can be deployed manually, but you probably want commit-to-deploy just like GitHub Pages provides.

Open up circleci.com and log-in via your repository provider (Bitbucket or GitHub). In your dashboard, go to Projects -> Add Project. Find your repository in the list, and click on the ‘Build project’ button next to your repo. Circle CI will immediately try to build your site, which will likely fail since you have not told it how to do so.

That build configuration is done via the special file called circle.yml which needs to be present in the root of your repository. Create that file and paste the following in:

# The Firebase tools want a v6.x version of Node but Circle CI
# defaults to v4.x. Set the latest LTS version.
machine:
  node:
    version: 6.10.3

# Need to install firebase-tools so that the deploy works
dependencies:
  pre:
    - npm install -g firebase-tools

# Need to tell Circle CI how to build the site
compile:
  override:
    - bundle exec jekyll build --verbose

# Circle CI expects some kind of testing to pass in order for the
# build to be successful and deploy. Since you don't have tests
# you can fake it by just returning true. http://gph.is/1MLPDWK
test:
  override:
    - "true"

# How you tell Circle to deploy to Firebase. The important thing to
# note here is the FIREBASE_TOKEN env variable. See below.
deployment:
  production:
    branch: master
    commands:
      - firebase deploy --token=$FIREBASE_TOKEN --non-interactive

Hopefully the file and comments are self-explanatory. As mentioned in the last stanza, there’s an environment variable which needs to be setup called $FIREBASE_TOKEN.

Firebase CI token

For the firebase tool on the CI server to be able to deploy your built site, it needs a token which grants it access at any time. That is passed to the tool via an environment variable as you saw above, but first need to obtain an authorization token.

Open up your terminal again at your site root, and run firebase login:ci. After you have granted access, you’ll hopefully see a token in your terminal window like this:

✔  Success! Use this token to login on a CI server:

1/gb4Gqp8hn31xNehCRcD939xpk7g4h1R7fycEwFTqPaw

Example: firebase deploy --token "$FIREBASE_TOKEN"

Copy that token and navigate to your project’s settings on the Circle CI website. Under ‘Build Settings’, click on ‘Environment Variables’, and then click on the ‘Add Variable’ button. Enter FIREBASE_TOKEN as the name, and paste the auth token as the value.

Commit the circle.yml

With your Circle CI project now set up, you can commit the circle.yml file and push it to your remote repository. Circle CI will pick up the change and hopefully do the build and deploy dance! 💃. You can watch along on the Circle CI website.

Move domain to Firebase Hosting

Hopefully you now have a working site at the Firebase URL provided to you. You can point your domain over to the the project. Go back to your project on the Firebase Console and click the ‘Connect Domain’ button. Follow the instructions and your site will now be hosted fully on Firebase.

Well done 👏.


Phew, that was a lot of work. In the next blog post I’ll talk about how I’ve over-engineeredtweaked this setup even further.