For almost two years, I’ve been using GitHub Pages to host my blog. This tool handles a lot of things for you behind the scene: it builds your site and deploys it to a web server. GitHub implements this as a great example of continuous delivery, the marriage of continuous integration with continuous deployment. GitHub also lets you use your own custom domain name, but it doesn’t support TLS connections over them. Thus, I had to take matters into my own hands and go back to self hosting.
Enter my old pals NearlyFreeSpeech.NET, Let’s Encrypt, and Travis CI. This host provides SSH access, and Travis provides easy ways to set up CI/CD pipelines. Let’s get started on building the GitHub Pages site first which itself is based on Jekyll. This static site generator is written in Ruby, so in order to build this, we need a recent Ruby environment. Then a few commands can get you bootstrapped:
gem install bundler bundle install bundle exec jekyll serve
This command serves an incrementally compiling view of the site.
We can change
build to simply get the output.
Now we need to deploy these output files via SSH.
There are several different ways to copy files over SSH, and in today’s post, we’ll be piping data directly through an SSH pipe.
tar -czf - -C _site . | ssh email@example.com 'tar -xzf - -C /var/www'
tar, we bundle up the entire
_site/ directory and compress it, and we output to stdout.
By changing directories into what we want to compress (using
-C), we’ll be able to easily extract it wherever we want.
Now that we have the basics, let’s integrate it with Travis.
First, let’s get started with continuous integration of our website.
We need to add a minimal
.travis.yml config file to declare our pipeline:
language: ruby cache: bundler before_install: - gem install bundler script: - bundle exec jekyll build
In our minimal config, we declare the programming language in use (Ruby), enable package caching for Bundler, setup commands to prepare the build environment, and build commands. After enabling our repository in Travis, we can now see our site get built on commit. This can help detect errors in configuration or anything verifiable that we add tests for.
Next, we need to configure deployment. Travis provides a simple way to encrypt files to store in a git repository which can be decrypted inside the running Travis build container. We’ll use this to encrypt an SSH private key file to be used for copying our site build artifacts. Let’s set up a deploy key.
echo .travis_deploy_key >>.gitignore ssh-keygen -C 'firstname.lastname@example.org' -f .travis_deploy_key
Next, we need to install the Travis command line tool and log in.
gem install travis travis login
Next, we encrypt the file and update our Travis config.
travis encrypt-file .travis_deploy_key -a
This will output a file named
.travis_deploy_key.enc which can be safely committed to git.
The unencrypted file should not be committed, and the provided example adds it to
.gitignore to prevent it as such.
Now we can add some more settings to
language: ruby cache: bundler addons: ssh_known_hosts: ssh.example.org before_install: - openssl aes-256-cbc ... - gem install bundler script: - bundle exec jekyll build after_success: - eval "$(ssh-agent -s)" - chmod 600 .travis_deploy_key - ssh-add .travis_deploy_key - "tar -czf - -C _site . | ssh email@example.com 'tar -xzf - -C /var/www'" branches: only: - master
Of note here is the
The first three commands are used to set up
ssh-agent so that any
ssh program can use its keys without user input.
The last command is the same one from earlier for deploying the site artifacts.
We also have a
branches section which we use to restrict this pipeline to only execute on the master branch.
This way we can use branches to store blog post drafts and other things without them being deployed as well.
We can adapt this process to build unsupported programming languages as well such as LaTeX.
To do so, we can use the
apt addon for Travis.
addons: ssh_known_hosts: ssh.example.org apt: packages: - texlive-full before_install: - openssl aes-256-cbc ... script: - pdflatex cv.tex - pdflatex cv.tex after_success: - eval "$(ssh-agent -s)" - chmod 600 .travis_deploy_key - ssh-add .travis_deploy_key - scp cv.pdf firstname.lastname@example.org:/var/www/ branches: only: - master