Anthony Chu Contact Me

Hosting Jekyll on Azure App Service on Linux

Tuesday, February 28, 2017

Microsoft announced Azure App Service on Linux back in September. On the surface it looks just like the App Service we know and love, except now running on Linux. But under the covers it's quite different and uses Docker extensively.

Thanks to its support of Docker, we can use almost any Linux-based Docker image in a Linux Web App. Today we'll look at how to set up App Service on Linux to build and serve a Jekyll site that is deployed continuously from GitHub.

App Service on Linux architecture

Not a ton has been shared about the architecture of App Service on Linux, so here is a simplified diagram based on how I think it works (almost certain to contain some inaccuracies):

App Service Linux Architecture

As far as I know, the load balancer, use of IIS ARR (yep, running on Windows), and the underlying file server are almost exactly the same as App Service on Windows.

What is different is the box I labelled "App Service Plan". It's a Linux VM (or VMs) that run the Docker engine. For each Web App, there is a container that runs the main app. There's also a container for Kudu; it looks like it's using a port of Kudu that's running on Mono (?).

App Service for Linux comes with a bunch of preconfigured containers such as Node and .NET Core.

App Service Linux

As the architecture diagram above shows, each container has its /home directory mounted from the file share. This means while the container has everything we need to run the application, the actual application itself is located outside of the container. This allows the Kudu container to also see the application folder. It also allows continuous deployment to deploy to the shared folder and the application is automatically updated without restarting the container.

To get more insight into how these built-in containers work, their dockerfiles are all available at https://github.com/Azure-App-Service.

Custom images

Because App Service for Linux can run pretty much any Docker image on Docker Hub (public or private) and on a private Docker registry, we are able run customized versions of the built-in images or completely new images altogether.

App Service Linux

We'll leverage this capability to host Jekyll.

Jekyll custom image

Jekyll is a static CMS. I'm currently using it for this blog. For more information on how I use Jekyll, check out my previous articles:

To get started, we'll create a custom image for running Jekyll. Thankfully, there's already an official image for Jekyll on Docker Hub that we can use.

The dockerfile that we'll be using is really simple:

FROM jekyll/jekyll

CMD ["jekyll", "serve", "--force_polling", "-s", "/home/site/wwwroot"]

All we have to do is add a startup command to run jekyll serve. We're setting the source to /home/site/wwwroot which is automatically mounted by App Service.

When it builds the site, it'll output its content to /srv/jekyll by default and serve it from there. We could have outputted the compiled site into /home/site/wwwroot as well, but if we have more than one instance of the container running, all of them writing to the same share might cause bad things to happen.

Now all we have to do is build the image from our dockerfile and push it to Docker Hub.

Setting up App Service on Linux

We'll start by creating a new Web App on Linux in the Azure Portal. In the "Configure container" blade, specify our custom image from Docker Hub:

App Service Linux

Note that we could have just used the jekyll/jekyll image directly and set the Startup Command to run what we have in our dockerfile. But I wasn't able to get it to work.

After the Web App is created, we can try browsing to it. But it won't work! This is because our container exposes port 4000, but by default App Service uses port 80. Luckily this setting is configurable.

Go to Application Settings and add a setting named PORT and set it to 4000. We also see that the custom image we specified is just another application setting value.

App Service Linux

Now we check out site again and we should see a directory listing of our wwwroot folder (it's actually copied its contents and serving it from /src/jekyll). We can tell it's our container doing the work because it says the web server is WEBrick and it's running Ruby.

App Service Linux

If we peek at the developer tools, we can see that it indeed is being served through IIS and we have a cookie for sticky sessions if ARR affinity is turned on in our settings.

App Service Linux

We're now done setting up Azure App Service to serve a Jekyll site. We just have to find a site to deploy.

Continous deployment from GitHub

We'll deploy a barebones, sample Jekyll site from GitHub. To do that we need to set up Azure App Service continuous deployment. The steps to do this are exactly the same as for Windows, so there are no surprises here.

App Service Linux

Once deployment is complete, reload our site! Jekyll should have noticed the files have changed and recompiled the site.

App Service Linux

If we need to make and changes to our site, make the change locally and push it to GitHub. It'll automatically deploy to our Web App!

Possible improvements

I'm not sure how production-ready the WEBrick web server is. One thing we can do to improve this is use NGINX to serve the files and change the jekyll serve to jekyll build --watch.