Deploying a Custom Build of Zola to Netlify

  • 30 May 2021

I’ve recently started to enjoy building web sites using Zola, a Rust-based static site generator. I’ll write more about my decision to use Zola in a future “how I built this” article, but for now I wanted to share with the Zola community some tips on using custom builds of Zola.

The Goal

I have several sites that I have either built or am about to build using Zola (this being one of them!). I uncovered a bug in how the next/previous page links are constructed when using transparent sections.

The bug (a race condition) often rendered my intended site structure with missing links to next and previous pages.

Good news: The fix was pretty easy.

Bad news: The fix was not likely to appear in a released version for some time to come.

As mentioned in the forum thread, not having access to this bug fix was going to cause me some heartache. Vincent was kind enough in follow-up discussion to suggest a workaround: I could publish on Netlify with a custom build of Zola.

This is what I’m doing, both on this site and on several others that I am either building or have recently deployed.

Constructing a build that ran locally was pretty easy; I backported PR #1456 onto the existing tagged 0.13.0 release of Zola. The end result is available on my fork of Zola and is tagged in that fork as release 0.13.0.es2. As with anything open-source, you are welcome to use it directly or adapt it for your own purposes.

Getting that build running on Netlify turned out not to be a particularly simple thing to do.

I am sharing how I did this in hopes that it might make life easier for the next person who walks this path.

The Machines in Question

My personal development environment is an Intel-based Mac laptop that I haven’t (yet) updated to Big Sur.

Netlify runs Zola (or whatever SSG you are using) in a custom build image that is based on Ubuntu 16.04. Netlify comes prepared to install and run recent released versions of Zola (up to the now-current 0.13.0) via their binrc tool.

What Didn’t Work

If you’re eager for answers, feel free to skip ahead to What Did Work below.

If you’re up for a war story, here are the false starts:

Compiling the next Branch

I started by trying to build the next branch as of commit 22c29fe, which is when my PR was merged. This failed in two different ways:

  • Cross-compiling. My current-ish (1.52.0) Rust toolchain failed to build the x86_64-unknown-linux-gnu target with this error:
error: failed to run custom build command for `ring v0.16.19`
  • Building the next trunk with my PR merged via the existing Docker container. I noticed that there was a Docker image in the repo, so I tried to use that to build Zola. I was thinking I could extract the binary and post that to a place Netlify could access. I found that somewhere between 0.13.0 and the merging of my PR, the Docker container stopped working. Running docker build --progress plain . on that commit failed with the following message (edited for legibility):
#7 97.45 error: could not compile `minify-html`
#7 97.45 
#7 97.45 Caused by:
#7 97.46   process didn't exit successfully: `rustc --crate-name minify_html --edition=2018
           /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/minify-html-0.4.10/src/lib.rs
           (options omitted)
` (signal: 9, SIGKILL: kill)
#7 97.46 warning: build failed, waiting for other jobs to finish...
#7 119.8 error: build failed
#7 ERROR: executor failed running [/bin/sh -c cargo build --release --target x86_64-unknown-linux-gnu]: exit code: 101
------
 > [builder 5/9] RUN cargo build --release --target x86_64-unknown-linux-gnu:
------
executor failed running [/bin/sh -c cargo build --release --target x86_64-unknown-linux-gnu]: exit code: 101

Backporting to 0.13.0

I then gave up on the next branch and started building the custom branch I mentioned earlier. Again, this is 0.13.0 with a backport of my PR and some yet-to-be discussed tweaks to the build infrastructure.

  • Building via the existing Docker container. I again used the existing Docker container, this time to build my custom branch. This build succeeded, but then failed to deploy into the Netlify environment because of a system library mismatch. I don’t have the deploy logs for that handy any more, but i think it was glibc vs MUSL if I recall correctly.

Researching on this build failure led me to the fact that the default Rust container uses Debian and Netlify uses Ubuntu.

So, I started doing major surgery on the Dockerfile with the intent of building Zola directly on Ubuntu and mimicing the build steps in Zola’s Azure pipeline as closely as possible. See my commit e19dd2c for details:

That led to the next few failures:

  • Using binrc isn’t an option for arbitrary binaries. I thought, based on my reading of the binrc documentation that I could post a release on GitHub and have Netlify download that release.
10:11:57 AM: ────────────────────────────────────────────────────────────────
10:11:57 AM:   1. build.command from netlify.toml                            
10:11:57 AM: ────────────────────────────────────────────────────────────────
10:11:57 AM: ​
10:11:57 AM: $ binrc install scouten/zola 0.13.0.es2 && zola build --base-url $DEPLOY_PRIME_URL
10:11:57 AM: zola's version v0.13.0.es2 doesn't match any known constraint, binrc cannot install it

I’m not 100% sure, but I read this as saying that binrc is configured to only download from pre-approved repos and mine isn’t one of them.

So … back to the curl trick as originally suggested by Vincent. Next failure:

  • Building on Ubuntu 16.04 failed. I don’t have a log handy any more, but Rust was unable to generate a viable build when running on 16.04.

  • Building on Ubuntu 20.04 resulted in a binary that wouldn’t deploy on Netlify.

8:42:31 PM: $ curl -sL https://github.com/scouten/zola/releases/download/v0.13.0%2Bes1/zola -o ./zola && chmod +x ./zola && ./zola build --base-url $DEPLOY_PRIME_URL
8:42:32 PM: ./zola: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.27' not found (required by ./zola)

This appears to be documented in the Rust world as follows:

Further reading led me to Netlify’s build image selection panel. There, waiting for me, was a button that I could click to switch to deploying using Ubuntu 20.04.

And that leads me to …

What Did Work

So, having eliminated the possibilities above, I’ll summarize the steps that ultimately did work for me: 🎉

Custom Build of Zola

I started from the tagged 0.13.0 release of Zola and added just the bug fix I wanted. This build is in the custom branch of my fork of Zola.

Custom Docker Build

In the above branch, I made changes to the Dockerfile to allow me to generate a version of Zola that can deploy on Netlify.

This Dockerfile differs from the one in 0.13.0 as follows:

  • It starts from the exact (newer, non-default) OS release used by Netlify, Ubuntu 20.04.
  • It adds the Rust build tools in essentially the same fashion as the official Rust build image.
  • It builds the release target, but does not dockerize it.
  • It packages a .tar.gz file such that I can subsequently shell into the Docker image and extract that file.

As of this writing, I don’t plan to submit this branch as a PR to Zola because the bug fix PR has already been accepted for the next major version of Zola and the Dockerfile changes serve a different purpose than the original Docker image. (Mine isn’t a deployment container, but rather a build vehicle.)

Change Netlify Build Image

In the Netlify control panel for my site (Site page > Site settings > Build & deploy > Continuous deployment > Build image selection > Edit settings), I changed the choice of image to “Ubuntu Focal 20.04.”

Build image selection

Change netlify.toml

One last change, and I made that using Netlify’s file-based configuration, also known as netlify.toml. Here is the netlify.toml file for this site:

[build]
publish = "public"
command = "curl -sL https://github.com/scouten/zola/releases/download/v0.13.0.es2/zola-v0.13.0.es2-unknown-linux-gnu.tar.gz | tar zxv && chmod +x ./zola && ./zola build"

[context.deploy-preview]
command = "curl -sL https://github.com/scouten/zola/releases/download/v0.13.0.es2/zola-v0.13.0.es2-unknown-linux-gnu.tar.gz | tar zxv && chmod +x ./zola && ./zola build --base-url $DEPLOY_PRIME_URL"

What’s important here are the build.command and context.deploy-preview.command entries, which download the custom build, extract it from the tarball, and then use it to render the site content. (I’ve chosen to host the custom build as a GitHub release, but it appears you could use any publicly-accessible location that is convenient to you.)

Feedback

I hope this helps. I choose not to have comments here, but if you want to respond or suggest edits, I’ll watch the corresponding post on the Zola forum (link to be added soon) for feedback.