Static Site Generators offer a great means of easily authoring content without needing to worry about the pain of some kind runtime. Given that the target hosting location for such sites in today's cloud world are blob stores exposed as websites or other cheap hosting options if they come with any kind of hosting option it is likely aimed at development and author time.

So what then is the best way to host a site generated by one of these static site generators in Kubernetes?

Kubernetes is all about container orchestration - which is great if you can have all of the content / runtime for a site bundled up in a container for hosting. So hosting the output of an SSG in Kubernetes could be done in a strait forward manor by extending the Nginx container and copying the static site content into it.

The drawback of this approach is that you now have a container that is really only useful for one thing, and then only for as long as the content remains current. Now we could just publish only the one tag for the container, but this continues to feel at odds to the container ethos to me.

To this end I wanted built a Helm chart sets up a Kubernetes deployment that pulls a Git repository, runs Zola to generate the static site content then hosts the static content using Nginx.

Updating the site content is then as easy as pushing the change to the Git repository, and then restarting the deployment.

This is done through use of initContainers and emptyDir temporary storage.

The basic process is we define an emptyDir that will be the "scratch" space - a bit of filesystem that is accessible to each of the initContainers as well as the main nginx container.

spec:
  template:
    spec:
      volumes:
        - name: scratch
          emptyDir: {}

initContainers are executed one by one in the order they are defined, so by we can leverage this to pull in some basic containers for executing our fetch and build tasks

First the fetch container, where you can see we just clone the git repository. Since Zola templates are often just git repositories, I was first using a git submodules to install the template I was using - hence the --recurse-submodules flag. --depth= and --shallow-submodules both improve the clone speed by only pulling a limited history state. --branch here could be either a branch or tag name. You can see that the scratch volume is mounted into this init container, and that we instruct git to clone onto that volume.

- name: fetch
  image: "{{ .Values.image.git.repository }}:{{ .Values.image.git.tag }}"
  imagePullPolicy: {{ .Values.image.git.pullPolicy }}
  args:
    - clone
    - --depth=1
    - --recurse-submodules
    - --shallow-submodules
    - --branch={{ .Values.repository.branch }}
    - {{ .Values.repository.url }}
    - /scratch
  volumeMounts:
    - mountPath: /scratch
      name: scratch

Next up is the build container. Since we do not need to pass any arguments to zola build this one is much simple. Again we mount the scratch volume. We also set that volume as the working directory for the container, since zola build expects the current working directory to contain the site to be build.

- name: build
  image: "{{ .Values.image.zola.repository }}:{{ .Values.image.zola.tag | default .Chart.AppVersion }}"
  imagePullPolicy: {{ .Values.image.zola.pullPolicy }}
  workingDir: /scratch
  args:
    - build
  volumeMounts:
    - mountPath: /scratch
      name: scratch  

Last up is the actual nginx container that does the actual hosting. This is pretty much left intact from the helm create process. The main things to note are again, mounting scratch volume.

Well that is mostly a brain dump on how I setup hosing a Zola generated site in Kubernetes. If you stumble across this, I hope you find it useful.