Edit: Thursday November 15 2018

Note that I do not use this any more. The steps talked about here may still work with minimal changes and may be worth reading for the Go program, though keep in mind how old this is.

Friday February 12 2016

What this article is and is not

This article is a quick setup tutorial based on how I have chosen to run Jekyll and Nginx while using Git as my version control. This article will not explain in-depth Jekyll, Nginx, or Git.

Nginx and Jekyll on your remote server

Make sure that you have Nginx installed, I choose to run FreeBSD and as such Nginx comes with a pretty basic and centralized configuration. I tend to lean it out and add an include for a separate vhosts folder like so:

user  nobody;
# user  www;
worker_processes  2; # Number of cores

events {
        # worker_connections  1024; # Standard configuration
        worker_connections  10000; # Likely overkill
}

http {
        include       mime.types;
        default_type  application/octet-stream;

        #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
        #                  '$status $body_bytes_sent "$http_referer" '
        #                  '"$http_user_agent" "$http_x_forwarded_for"';

        #access_log  logs/access.log  main;

        sendfile        on;
        #tcp_nopush     on;

        #keepalive_timeout  0;
        keepalive_timeout  65;

        #gzip  on;
        # include inc/*;
        include vhosts/*.conf;
}

This is of course only an example configuration. Do what is right for your system, especially when it comes to the main configuration file.

The virtual host file that I’m using with Jekyll is as follows:

# Example letsencrypt command:
# letsencrypt certonly --webroot-path /tmp/lets -d www.example.com -d example.com
server {
        listen 80;
        # You may or may not have IPv6, so disable this if necessary
        listen [::]:80;
        server_name
                example.com
                www.example.com
                myotherexample.com
                www.myotherexample.com
        ;

        # For the Letsencrypt certificates
        # root /tmp/lets;

        # Otherwise:
        location / { return 301 https://www.example.com$request_uri; }
}
server {
        listen 443 ssl;
        listen [::]:443 ssl;
        ssl_certificate                 /usr/local/etc/letsencrypt/live/www.example.com/fullchain.pem;
        ssl_certificate_key             /usr/local/etc/letsencrypt/live/www.example.com/privkey.pem;
        server_name
            example.com
        ;

        location / { return 301 https://www.example.com$request_uri; }
}
server {
        # Note, to use HTTP/2 you will need an up to date mainline version
        # of Nginx. On FreeBSD this is called "nginx-devel"
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        ssl_certificate      /usr/local/etc/letsencrypt/live/www.example.com/fullchain.pem;
        ssl_certificate_key  /usr/local/etc/letsencrypt/live/www.example.com/privkey.pem;

        server_name
                www.example.com
                ;
        index index.html index.htm;
        root /var/www/example.com;
        autoindex on;

        # Handle Jekyll being separate but on the same level as everything else
        location / {
                try_files $uri $uri/ @jekyll;
                rewrite ^(.*)/$ $1/index.html last; # Allow for /index.html to be the index
        }
        # Any file that does not exist in our root will get tossed to the
        # Jekyll _site directory.
        location @jekyll { root /var/www/example.com/jekyll/_site; }
        # Do not allow anyone access to the /jekyll directory directly
        location /jekyll { deny all; }

        # If I were to put a picture in here I'd like to call it without
        # the file extension, just a personal preference.
        location /pictures {
                autoindex on;

                try_files $uri $uri.png $uri.jpg $uri.gif $uri/ /;
        }

        # If I'm going to add some files for the world to see there is no
        # reason to have autoindex off
        location /files {
                autoindex on;
        }

        # This is going to be used by our webhooks to auto update our website
        location /git-autoupdate {
                include inc/proxy.conf;
                # Make sure this matches your ~/.gohook.yaml
                proxy_pass http://127.0.0.1:10000/;
        }
}

You’ll note that I’m using Let’s Encrypt here, even providing an example command although it’s not required to run a domain with a SSL certificate I highly recommend that you do not skip the SSL certificate. It not only protects the privacy of others but it also protects your content from modification or censorship by malicious parties

Now for the Jekyll setup,

I will assume that you have a local Jekyll site and already have its contents in a git repository. The commands below will clone your remote repository into the jekyll folder

$ export REPO_URL="https://github.com/username/example.com"
$ cd /var/www/example.com # The root of your website, this should match your Nginx config
$ git clone "$REPO_URL" jekyll # clone into the Jekyll folder
$ jekyll build # Initial Build of the website
$ cat > .git/hooks/post-merge <<EOF
#!/bin/sh
# This script will make sure your site is rebuilt on every git update
jekyll build
EOF
$ chmod +x .git/hooks/post-merge # Make sure that it's executable

Automatically have your Jekyll site updated

when you run a git push To accomplish this I’m going to leverage webhooks provided by server side software such as Github, Gogs, and Gitlab. First things first let’s make sure that we have our application setup to receive the web hook

I’ve written a small application in Go to make this easy. You can also find the source code and some extra documentation on my git server. You may need to install go, some distributions provide go as a package, you can also follow the instructions on their website.

Setup commands:

$ go get git.riedstra.us/mitch/gohook
$ cd $GOPATH/src/git.riedstra.us/mitch/gohook
$ go install
$ cp gohook.yaml ~/.gohook.yaml
$ $EDITOR ~/.gohook.yaml

~/.gohook.yaml

# Address and port the Go "net/http" server will listen on
# Make sure this matches the Nginx configuration above
listen: 0.0.0.0:10000
# Map of URLs to look for in the hook, with options
repos:
   https://gogs.example.com/bob/site:
      dir: /var/www/example.com/jekyll
      branch: master
      location: origin

To make sure this runs on boot can differ for practically every system in existence. I just have it setup to run from the crontab.

$ REPO_OWNER="www" crontab -e -u $REPO_OWNER

Contents of the crontab:

# Note, you will need to set PATH and HOME, e.g.
PATH="/usr/home/www/bin/go"
HOME="/usr/home/www"

# Automatically update repositories
@reboot gohook >/dev/null 2>&1 &

Run gohook without rebooting:

$ gohook >/dev/null 2>&1 &

Go ahead and test your setup

At this point everything should be set up to allow you to push from your local machine to your git server ( or service, such as Github ) and have your website update within a few seconds.