New title would be:
Evernote, Jekyll Static Site, Github, Cloudflare Pages and Azure DevOps

Updates on 8th May 2021

CloudFlare Pages became general available today. Moved hosting to CloudFlare Pages via a GitHub repo. This removed Azure Blob and CDN from the hosting architecture which simplifies a number of things including pipeline duration. Previously due to having to wait for Azure CDN cache clearing to finish then clear cache on Cloudflare, the pipeline takes up to 15 mins sometimes. Now it's just 1 min.

Updates on 22 May 2020

Azure just released Static Web Apps(Preview) which makes things interesting with what I am doing here. 

Based on a quick play around, it feels like a replacement for Github Pages as it’s quite tied to Github repo and Github Actions at the moment. It does offer a better experience than Azure Static websites with Storage and also Github Pages but before it provides more support for other systems and tools, and the “preview” tag removed, I will stick around with what I have now.

In short, this static website is now:
  • Authored and stored in Evernote (this is the same as before)
  • Posts are pulled from Evernote and generated by Jekyll static site generator (new)
    • Azure DevOps is used for these processes
  • Hosted on Azure Storage as a static website (new)
  • Accelerated via Azure CDN (new)
  • Further proxied via Cloudflare (this is the same as before)

I probably over-engineered it a bit but anyway it’s a fun weekend project.  Going to discuss a few things.

HTTPS on root apex domain

The goal was to enable, but
  • When using Azure CDN with Azure Storage static websites, Azure CDN doesn’t support CDN managed SSL certificate for root domains….
  • If Cloudflare is used instead of Azure CDN, root apex domain binding is not supported with Azure storage static website….

So the solution was to set up Azure CDN with root apex domain (no https) using Azure storage static website. Then set up Cloudflare for the root domain and turn on proxy, also need to turn on full SSL too in Cloudflare. The site is served with Cloudflare SSL certificate and cached twice in both Azure CDN and Cloudflare. 

A bit dumb I know, but this seems to be the only workaround for now.


In the previous version of the website, I built a custom Asp.Net website to pull Evernote data, generate a static website using a .Net tool: Pretzel and commit to Github repo because it was hosted with Github Pages. It works but a bit fragile due to so many parts and the .Net tool not being popular and maintained.

Now, all above runs in Azure DevOps. 

A Build pipeline - Runs on a Windows 2019 Agent
  1. Init and checks out git - about 13 seconds
  2. Pulls posts from Evernote into a Jekyll Site - about 6 seconds
  3. Setup Ruby Environment, Bundler install and builds the Jekyll site - about 22 seconds
  4. Writes Build version into a file and place in generated site - 1 second
  5. Publishes static website as artifacts for backup and history keeping - 8 seconds
  6. Uploads to Azure Storage and purges Azure CDN cache using Azure CLI - about 3.5 minutes
  7. Purges Cloudflare cache using a custom task (REST API) - about 1 second

Total runs about 4.5 minutes.
  • Purging Azure CDN takes about 2.5 minutes because it’s a synchronous command. Didn’t make it asynchronous because it is only valid to run Cloudflare purge when Azure CDN is finished with purging. 
    • Occasionally it takes about 15 minutes... Nothing I can do about it 🤷🏻‍♂️
  • The bundle install process initially took about 1 minute but brought down to only 22 seconds by
    • Using --no-document when running gem install, so it doesn’t install documentation. This saves about 10 seconds.
    • Setting BUNDLE_PATH in Azure Build Pipeline and point to a folder inside of git repo which contains all required gems. It increases about 3 seconds on git checkout time but saved about 30 seconds for bundle install.
  • Azure CLI takes about 30 seconds to initialise, so I combined Azure Storage upload and CDN purge into one task to save the extra 30 seconds.
  • I did try Azure Pipeline Cache. It works but
    • It clears cache after seven days of no use, doesn’t really help my case since I sometimes don’t update the blog for months.
    • The restore task takes about 10 seconds, and the install task is 10 seconds longer than my “brutal” approach. So in total about 20 seconds less effective.

I am happy enough so far and only wish that Azure CDN purge can take less time in the future. Or can just use either Azure CDN or Cloudflare, no need for both.

Building Jekyll with Ruby on Windows Agents

When running Ruby on Windows agents, use Powershell is easier. And because it’s a build agent, so we don’t have to reset JEKYLL_ENV value. Here is a sample script for Powershell:

cd $(Build.Repository.LocalPath)\JekyllSite\
gem install bundler:1.17.2 --no-document
bundle _1.17.2_ install --retry=3 --jobs=4
$env:JEKYLL_ENV = "production"
bundle _1.17.2_ exec jekyll build

If you use CMD. You need to prefix lines that invoke scripts with “call” like this:

cd $(Build.Repository.LocalPath)\JekyllSite\
call gem install bundler:1.17.2 --no-document
call bundle _1.17.2_ install --retry=3 --jobs=4
set JEKYLL_ENV=production
bundle _1.17.2_ exec jekyll build

I fix all the package versions, so it’s more predictable. For Ruby, I set it to 2.6.

The old and new architecture diagrams: