In the last post on Hugo, we quickly built a simple website with a minimalist Hugo theme. But what if we decide to switch our blog to a different theme? Is it really as simple as changing the name of the theme? Let’s find out!

Note: see the addendum for updates after publication.

Why change the theme at all?

After using the Hermit theme for a bit, I realized that the light grey text on a darker grey background is a bit hard to read due to low contract, and I wanted something with more contrast: darker text and a lighter background, which is more readable especially for longer content.

One option could have been to modify the theme’s colors and keep the theme, but I decided instead to look for other themes that might have a simple, minimalist look, but with the opposite color scheme. I finally found the Nuo theme and decided to adopt it.

Using the Nuo theme

As before, I added the theme to my Git repo as a submodule:

git submodule add https://github.com/laozhu/hugo-nuo themes/nuo

and modified the config.toml accordingly:

-theme = "hermit"
+theme = "nuo"

Let’s try it out and see how it works!

Using the new theme

Let’s create a new page:

$ hugo new about.md
[...]
ERROR [...] NUO theme does not support Hugo version 0.47.1. Minimum version required is 0.50
[...]

$ hugo version
Hugo Static Site Generator v0.47.1/extended linux/amd64 BuildDate: 2018-09-25T03:41:10Z

OK, we need to upgrade Hugo. I downloaded the latest version at the time (v0.55.5) and tried again:

$ ~/hugo/0.55.5/hugo new about.md
[...]/content/about.md created

After updating the new about.md page, let’s see what it looks like in our browser:

$ ~/hugo/0.55.5/hugo serve --verbose --disableFastRender

Looks good, let’s ship it!

Commit and build on GitLab

Creating a new branch for this change, pushing it to GitLab, and creating a pull request causes a new build to happen via GitLab Pipelines, and if successful, I merge into master and it is published to the live site. Here, however, it errored out with:

failed to transform resource: TOCSS: failed to transform
"styles/main-rendered.scss" (text/x-scss): this feature is not available in
your current Hugo version, see https://goo.gl/YMrWcn for more information

What does this mean? The URL in the error points to a help page on the Hugo website which says:

I get “TOCSS … this feature is not available in your current Hugo version”

If you process SCSS or SASS to CSS in your Hugo project, you need the Hugo extended version, or else you may see this error message:
error: failed to transform resource: TOCSS: failed to transform
“scss/main.scss” (text/x-scss): this feature is not available in your current
Hugo version

We release two set of binaries for technical reasons. The extended is what you get by default, as an example, when you run brew install hugo on macOS. On the release page, look for archives with extended in the name.

To confirm, run hugo version and look for the word extended.

Turns out, someone ran into the same issue as I did, and filed an issue with GitLab 2 weeks ago, but it hasn’t been fixed yet.

That said, I was really set on using the new higher-contrast theme, so what do we do in the meantime while this is not fixed?

Let’s build a custom container!

Let’s recall from our earlier post that GitLab Pipelines simply runs the hugo binary from a Docker container, so why don’t we build a custom Docker container and specify that for GitLab to use?

First, let’s look at our Dockerfile:

ARG ALPINE_VERSION="3.9"

FROM alpine:${ALPINE_VERSION}

ARG HUGO_VERSION="0.55.5"
ENV HUGO_OS "Linux"
ENV HUGO_ARCH "64bit"
ENV HUGO_ARTIFACT "hugo_extended_${HUGO_VERSION}_${HUGO_OS}-${HUGO_ARCH}"

ENV HUGO_EXT "tar.gz"
ENV HUGO_ARCHIVE "${HUGO_ARTIFACT}.${HUGO_EXT}"

ADD "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${HUGO_ARCHIVE}" /tmp

ENV HUGO_DIR "${HUGO_ARTIFACT}"
ENV HUGO_BINARY "hugo"

RUN mkdir -p "/tmp/${HUGO_DIR}" \
    && tar zxf "/tmp/${HUGO_ARCHIVE}" -C "/tmp/${HUGO_DIR}" \
    && rm -f "/tmp/${HUGO_ARCHIVE}" \
    && mv "/tmp/${HUGO_DIR}/${HUGO_BINARY}" "/usr/bin/${HUGO_BINARY}" \
    && rm -rf "/tmp/${HUGO_DIR}"

EXPOSE 1313

Pretty straight-forward, and nicely parameterized in case we want to build contains with other Hugo versions in the future.

Let’s build the container:

$ docker build . -t hugo-extended:0.55.5
Sending build context to Docker daemon  6.656kB
Step 1/13 : ARG ALPINE_VERSION="3.9"
Step 2/13 : FROM alpine:${ALPINE_VERSION}
 ---> 055936d39205
Step 3/13 : ARG HUGO_VERSION="0.55.5"
 ---> Running in 617ca0bf1849
Removing intermediate container 617ca0bf1849
 ---> 883260be520f
Step 4/13 : ENV HUGO_OS "Linux"
 ---> Running in 2195d86cf7a1
Removing intermediate container 2195d86cf7a1
 ---> 465fea446f70
Step 5/13 : ENV HUGO_ARCH "64bit"
 ---> Running in 984b4e67cc97
Removing intermediate container 984b4e67cc97
 ---> 3a61b75f0d3f
Step 6/13 : ENV HUGO_ARTIFACT "hugo_extended_${HUGO_VERSION}_${HUGO_OS}-${HUGO_ARCH}"
 ---> Running in 96a742c164b1
Removing intermediate container 96a742c164b1
 ---> 023e1e64d818
Step 7/13 : ENV HUGO_EXT "tar.gz"
 ---> Running in 9373b5d55389
Removing intermediate container 9373b5d55389
 ---> fc5e4f2e1fb2
Step 8/13 : ENV HUGO_ARCHIVE "${HUGO_ARTIFACT}.${HUGO_EXT}"
 ---> Running in 68aa09f179f9
Removing intermediate container 68aa09f179f9
 ---> 7d82c98b9a23
Step 9/13 : ADD "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${HUGO_ARCHIVE}" /tmp
Downloading [==================================================>]  9.409MB/9.409MB
 ---> cbad26b53db4
Step 10/13 : ENV HUGO_DIR "${HUGO_ARTIFACT}"
 ---> Running in 767e8f83433a
Removing intermediate container 767e8f83433a
 ---> 695fdff77a18
Step 11/13 : ENV HUGO_BINARY "hugo"
 ---> Running in 200121c634b5
Removing intermediate container 200121c634b5
 ---> de731ebf362c
Step 12/13 : RUN mkdir -p "/tmp/${HUGO_DIR}"     && tar zxf "/tmp/${HUGO_ARCHIVE}" -C "/tmp/${HUGO_DIR}"     && rm -f "/tmp/${HUGO_ARCHIVE}"     && mv "/tmp/${HUGO_DIR}/${HUGO_BINARY}" "/usr/bin/${HUGO_BINARY}"     && rm -rf "/tmp/${HUGO_DIR}"
 ---> Running in 275d08b83b12
Removing intermediate container 275d08b83b12
 ---> 7bce0d80a28a
Step 13/13 : EXPOSE 1313
 ---> Running in e557b975876b
Removing intermediate container e557b975876b
 ---> fddc6c95f029
Successfully built fddc6c95f029
Successfully tagged hugo-extended:0.55.5

Great! Looks like the build succeeded; let’s run it:

$ docker run hugo-extended:0.55.5 /usr/bin/hugo
standard_init_linux.go:207: exec user process caused "no such file or directory"

Wait, does this mean the binary doesn’t actually exist where we thought we put it? Let’s check:

$ docker run hugo-extended:0.55.5 ls /usr/bin/hugo
/usr/bin/hugo

So yes, it does, in fact, exist, and it’s in the expected location. So what could be causing the error?

$ docker run hugo-extended:0.55.5 ldd /usr/bin/hugo
	/lib64/ld-linux-x86-64.so.2 (0x7f64c9e96000)
	libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f64c9e96000)
	libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7f64c9e96000)
	libm.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f64c9e96000)
	libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f64c9e96000)
Error loading shared library libstdc++.so.6: No such file or directory (needed by /usr/bin/hugo)
Error loading shared library libgcc_s.so.1: No such file or directory (needed by /usr/bin/hugo)
Error relocating /usr/bin/hugo: _Znam: symbol not found
Error relocating /usr/bin/hugo: _ZNSo3putEc: symbol not found
[...]

Thus, looks like we’re missing shared libraries such as libstdc++ and libgcc_s from our image! But why?

After doing some research, we find other folks have run into the same issue, e.g., see

At this point, I realized that I could spend quite a bit more time on this, either extending my own Dockerfile, or using someone else’s, but then I realized that my goal was to write a blog, not hack on Dockerfiles! I also remembered that there’s a service called Netlify (highlighted by most Hugo themes) which provides very easy personal website hosting, and most importantly, it also claims to address one of my personal pet peeves with GitLab Pipelines and GitLab Pages: there’s no live preview of the site, simply a success/failure on the build run.

What if I want to see exactly what the site will look like when built?

GitLab Pages with a custom domain doesn’t support automatic certificate creation and renewal with Let’s Encrypt, so until recently, you may have noticed that my website was serving over HTTP, not HTTPS. GitLab does support you providing SSL certificates manually, but unlike GitHub, doesn’t automatically refresh them for you, and I don’t need to have another job of running a cron job to renew my certificates every 90 days (and if you don’t, your site stops working).

So now I had 2 strong reasons to consider an alternative to GitLab for hosting my site.

Enter the … Netlify

Turns out, Netlify also has a great integration with GitLab and can automatically build a preview of a branch from a merge request! And it trivially integrates with Let’s Encrypt and auto-renews! Sold.

I’ve since switched my hosting to Netlify, and now I can easily get a preview of each change before it goes live. And the site is now hosted on HTTPS. Win/win!

What’s the moral of the story? Sometimes, the solution to a technical problem is not an ever more complex Rube Goldberg machine in the form of an intricate Dockerfile with a custom build pipeline, but simply a change of approach, which brings with it additional benefits, with just a little up-front setup, and no ongoing work.

Addendum

This post was published on 9 May 2019; the GitLab issue with Hugo mentioned above as a key blocker was filed on April 12 and fixed on May 24. For me, the issue was solved by adding the following line to my Dockerfile:

--- Dockerfile.v1  2019-05-31 22:26:57.072002321 -0400
+++ Dockerfile.v2   2019-05-31 22:56:44.709374510 -0400
@@ -2,6 +2,8 @@
 
 FROM alpine:${ALPINE_VERSION}
 
+RUN apk add --update --no-cache libc6-compat libstdc++
+
 ARG HUGO_VERSION="0.55.5"
 ENV HUGO_OS "Linux"
 ENV HUGO_ARCH "64bit"

and all the symbols now resolve:

$ docker run hugo-extended:0.55.5 ldd /usr/bin/hugo
	/lib64/ld-linux-x86-64.so.2 (0x7f56c1b87000)
	libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f56c1b87000)
	libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x7f56c1a32000)
	libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7f56c1b87000)
	libm.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f56c1b87000)
	libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7f56c1a1e000)
	libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f56c1b87000)

Rather than deploying my own Docker image, I switched my .gitlab-ci.yml to point to the new Docker image registry.gitlab.com/pages/hugo/hugo_extended:0.55.6 with the Hugo extended binary and my GitLab CI builds now pass. I’m still using Netlify to preview and deploy the site.

Additionally, I recently switched to the Even theme for its simplicity; I may change the theme again, but now it’s much easier as a result of the changes described above!

And on a related note, another blogger described the full end-to-end process for launching a personal blog with GitLab and Netlify (just like this blog!), and it was discussed on Hacker News.