Using Docker for Hexo Content Generation

2020-02-04

Using Docker to easily generate a Hexo blog writing environment

Some of you have probably noticed that this blog is generated through Hexo.
Right now it’s a blog using Cactus as it’s theme

Edit: Used to be Hexo, now it’s generated through Hugo and the Ficurinia theme

It’s super simple and easy for me to maintain.

However the main issue I have is that I swap computers all the time.

I have about 3 computers I swap with constantly. these are my:

  • T440p
  • x230 Tablet
  • Custom built Ryzen 1800x desktop

While I could run Hexo on all 3 as needed, there are a few issues that can come up.

  1. Installing NodeJS through a package manager can have it update to a version that Hexo may not support until they release updates to the hexo-cli app.
  2. I could use NVM to keep my machines on the same version, but then I have to maintain 3 different nodejs installs manually, and NPM issues can crop up due to differences.
  3. I have a chance to fix this with Docker.

So rather than looking up if someone else has a solution already, I built my own

Originally, I just had this compose file:

version: '3'
services:
    hexo-dev:
        image: docker.io/library/node:10.15.3
        volumes:
            - "./blog:/code"
            - "./scripts:/app"
        ports:
            - 4000:4000
        entrypoint: /app/docker-entrypoint.sh

and this bash file

#!/bin/bash

if [ ! -d "/code" ]; then
    echo "Map a repo to the /code for this image to work"
    exit 1
fi

cd /code
npm install
npm install -g hexo-cli
hexo generate
hexo server -p 4000 -i 0.0.0.0

It works… However it writes all files a root, so permission issues crop up while working on it. While I could just preprogram a new user with the UID and GID parameters I need. I gave one of my friends access to the source files here so they can easily work on their blog as needed.

So I couldn’t exactly leave it as is, well a month later I can say eat your heart out Zav.

Beyond this is history

Initial Ideas

Originally, I wanted to see if I could adapt some code from linuxserver.io’s base containers for their abc user and graft it to the debian node images.

While this does seem like a good idea, I would have had to make a massive bash script with probably a fair amount of spaghetti code.

The next idea I had was installing node into linuxserver.io’s base ubuntu image. That worked somewhat, however node and s6 has some strange permission issues that cropped up.

Specifically about the $HOME environment variable. I had the program run as the user abc, yet $HOME was set to /root. Even setting this manually didn’t fix every issue.

Enter NVM

I then was able to use NVM to solve these issues.

Originally I had NVM auto install and build all in one container, but the container was thicker than needed, so I setup a multi-stage build

FROM ubuntu:bionic AS nvm
ENV NVM_DIR=/nvm
RUN \
    apt-get update && \
    apt-get install -y curl git && \
    mkdir "$NVM_DIR" && \
    git clone https://github.com/nvm-sh/nvm.git "$NVM_DIR" && \
    cd "$NVM_DIR" && \
    git checkout `git describe --abbrev=0 --tags --match "v[0-9]*" $(git rev-list --tags --max-count=1)` && \
     \. "$NVM_DIR/nvm.sh" && \
    nvm install 10.15.3

It starts with a simple ubuntu container that installs curl and git, then follows the manual install for NVM.

Finally it installs nodejs 10.15.3, the default version gitlab ships with it’s template container.

With this, I import the /nvm directory into the next container

FROM lsiobase/ubuntu:latest AS docker-hexo
LABEL maintainer="Isaac Towns <[email protected]>"
COPY --from=nvm /nvm /nvm
# Installs build-essential for npm compilation needs.
RUN \
    apt-get update && \
    apt-get install -y build-essential && \
    apt-get clean -y && \
    rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/*
ADD ./root /

Finally in the root directory in the source repository there is a couple of simple bash scripts.

/etc/cont-init.d/50-config

#!/usr/bin/with-contenv bash

if [ ! -d "/code" ]; then
    echo "Map a volume to /code for this image to work"
    exit 1
fi

usermod -d /config abc
chown -R abc:abc /config
chown -R abc:abc /nvm
chown -R abc:abc /code

/hexo.sh

#!/usr/bin/with-contenv bash
HOME=/config

export NVM_DIR="/nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
nvm use 10.15.3

cd /code
npm install
npm install -g hexo-cli
hexo generate
hexo server -p 4000 -i 0.0.0.0

With both of these bash scripts, the container is able to activate NVM and install hexo as needed.

If anyone wants to run the container, it’s available at https://gitlab.com/Zelec/docker-hexo