Dev Notes

Software Development Resources by David Egan.

Jekyll Deploy Methodology

BASH, Deployment, Jekyll, Rsync, SSH
David Egan

This post outlines a simple and secure method for deploying a Jekyll site to a server/VPS by means of SSH using Grunt and Bash.

The objective is to deploy a website by means of a Grunt task without exposing senstive information (SSH port, server IP address, username etc) in the Gruntfile. This allows the Gruntfile to be added to your VCS.

This is achieved by running a custom Bash script, which is executed from within Grunt.

Setting up Grunt Exec

The grunt-exec plugin allows shell commands to be executed from within Grunt.

Install grunt-exec with Yarn:

yarn add grunt-exec --dev

or install grunt-exec with NPM:

npm install grunt-exec --save-dev

The configuration for grunt-exec might look like this:

/* /grunt-config/exec.js */
module.exports = {
  build: {
    cmd: 'bundle exec jekyll build'
  dev: {
    cmd: 'bundle exec jekyll build --watch --config _config.yml,_config_dev.yml --drafts'
  serve: {
    cmd: 'bundle exec jekyll serve --watch'
  deploy: {
    cmd: 'deploy-my-website'

Note that this config is separated out into a separate file, which requires the load-grunt-configs plugin.

The Gruntfile

Your deploy tasks can now be defined in the project Gruntfile.js:

module.exports = function(grunt) {

    // Define scope for `load-grunt-tasks` package.
    var tasks = { scope: ['devDependencies', 'dependencies'] };

    // Define location of config files for `load-grunt-configs`.
    var options = { config: { src: "grunt-config/*.js" } };

    // Require `load-grunt-configs` with the defined options.
    var configs = require('load-grunt-configs')(grunt, options);

    // Load the various task configuration files.

    // Auto-load tasks.
    require('load-grunt-tasks')(grunt, tasks);

    // Register tasks
    // -------------------------------------------------------------------------
    grunt.registerTask('default', [

    grunt.registerTask('deploy', [


Now from within your project directory running grunt deploy will run the exec:build task (bundle exec jekyll build), followed by the exec:deploy task.

The exec:deploy task runs a BASH script called deploy-my-website - which should be held locally, outside your version control system.

BASH Deploy Script

I like to organise deploy scripts into a dedicated directory, creating symlinks to the files from with my /usr/local/bin directory (or somewhere within your $PATH).

The deploy script should be executable:

# Create symlink
sudo ln -s ~/deploy-scripts/deploy-my-website /usr/local/bin

# Make executable
sudo chmod +x ~/deploy-scripts/deploy-my-website

The deploy script utilises Rsync to copy files across to the server:

# deploy-my-website
# Deploy to
# ------------------------------------------------------------------------------
PORT=4321                                           # Custom port for SSH
USERNAME=my-user                                    # Username for the server
SERVER_IP="XXX.XXX.XXX.XXX"                         # Server IP address
REMOTE_PATH="/var/www/html/" # Server path
LOCAL_PATH="/var/www/html/"       # Local path
SITENAME="My website"                               # Site name

function deploy() {

    # Rsync options
    OPT=(--progress -a -v -rz --checksum --delete -e "ssh -p ${PORT}")



echo "Deploying ${SITENAME} to PRODUCTION..."


In our case, SSH is configured to only allow connection by means of SSH Keys - Public Key Cryptography/Challenge Response Authemtication. This makes the deployment process very secure - the local private key (passphrase protected for extra security) is necessary to establish the connection to the server.


comments powered by Disqus