Introduction

As I told before, I have this idea of putting everything I can to run from Docker containers, I am not sure that is the best way to do it, but I am doing it nonetheless.

What I have done until now, is create service containers, which are containers that keep running as service daemons, like Varnish, WordPress or Caddy, but now I want to have an image and create a container from it to run middleman on any machine or server, without the need to install ruby or anything directly on the server, I already have docker on it.

The main purpose

What I like to accomplish is that I am going to push my blog to the server, and with a git post-receive hook, I will checkout, run middleman container, build the blog, copy to web-server root folder, destroy the container, and there you have, something like Netlify or Vercel but selfhosted.

Dockerfile

I have gone with a Dockerfile to create my own image, will all I need to run Middleman app, I have tried to do it with Alpine, but keep getting error when running middleman build, so I have gone with the default Debian image, but its size is the double.

Here is the Dockerfile that works:

FROM ruby:2.6
LABEL maintainer="contact@garron.me"
RUN apt-get update -qq && apt-get install -y build-essential
RUN apt-get install -y nodejs
ENV APP_HOME /myapp
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile $APP_HOME/Gemfile
ADD Gemfile.lock $APP_HOME/Gemfile.lock
RUN gem install bundler:2.1.4
RUN bundle install

Creating the container

Once with that Dockerfile, it is time to create the container, use the next command do it.

docker build -t my-middleman .

That command will create the container with middleman in it, which I am going to use to build the site on the server as soon as I push to it a new post.

Preparing the server

Now the server needs to be ready to receive and deploy, so I created a bare git repository

mkdir $HOME/git/myblog.git && cd $HOME/git/myblog.git && git init --bare

And create this post-receive hook

vim /$HOME/git/myblog.git/hooks/post-receive

Add this content to it:

#!/bin/bash
set -eu
TARGET="/tmp/garron.blog"
GIT_DIR="/home/ggarron/git/garron.blog.git"
BRANCH="master"

while read oldrev newrev ref
do
        # only checking out the master (or whatever branch you would like to deploy)
        if [ "$ref" = "refs/heads/$BRANCH" ];
        then
                echo "Ref $ref received. Deploying ${BRANCH} branch to production..."
                unset GIT_INDEX_FILE
                git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f 
                cd $TARGET
                docker run --rm -v $PWD:/myapp my-middleman middleman build
                if [ $? -eq 0 ]; then
                        rsync -cazv --delete $TARGET/build/ /var/www/www.garron.blog/
                else
                        echo FAIL
                fi
        else
                echo "Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server."
        fi
done

This will run when something is pushed to this repo, if the change has been done in the chosen branch it will checkout to the working directory, and then build the site with the next command.

docker run --rm -v $PWD:/myapp my-middleman middleman build

That will build the blog, the last line is going to move the recently built pages to the web directory using rsync, only if middleman succeeded.

Conclusion

I managed to have a setup that works like Netlify, I just need to push to my web server, and my blog is deployed there.

This way I do not have to work on Linux or Mac, I can write on my Windows using git-bash, or on my iPad using Working Copy.