In this blog post I show you how Drone can be used to automatically build and deploy a Jekyll site. Drone is a very lightweight CI/CD platform which uses Docker containers to execute pipelines which are defined in simple YAML configuration files. The configuration shown in this post is also used for building and deploying this blog when changes are pushed to the corresponding repository. Let’s start by looking at a simple pipeline configuration for a Jekyll site.

Pipeline Configuration

To define the pipeline which should be executed by Drone, a file named .drone.yml has to be created in the root of the project repository. This file contains configuration parameters and code for each stage of the pipeline.

Build Stage

We start with a simple stage for building the site. In this stage, the standard ruby Docker image is used and two commands are executed in the root directory of the project. bundle install installs the necessary ruby gems and bundle exec jekyll build builds the site using the Jekyll gem. The pipeline is executed when commits are pushed to the master branch of the repository.

pipeline:
  build:
    image: ruby
    commands:
      - bundle install
      - bundle exec jekyll build

  branches: master

Deploy Stage

To deploy the site built in the previous stage, we define a second stage which copies the generated _site directory to a server with rsync. For this, we use the Drone plugin drillster/drone-rsync. We specify some parameters like the target host, the user or the source and target directory. In this configuration, the plugin uses public key authentication, where the corresponding private key is defined by a Drone secret. Details of how to specify the private key secret can be seen in the plugin documentation.

  deploy:
    image: drillster/drone-rsync
    hosts: [ "lafl-server.florian-latifi.at" ]
    user: blog
    source: _site
    target: /var/www/blog/
    recursive: true
    delete: true
    secrets: [ plugin_key ]

Notify Stage

Finally, we want to be notified when the pipeline execution succeeds or something fails during execution. Here, we use the Drone plugin drillster/drone-email. This plugin allows to send emails to a given address through a specified SMTP host. We define parameters for the SMTP host and port and the FROM mail address.

  notify:
    image: drillster/drone-email
    host: lafl-server.florian-latifi.at
    port: 25
    from: Drone <drone@lafl-server.florian-latifi.at>
    when:
      status: [ failure, success ]

Caching Gems Installed by Bundler

To speed up builds, we want to cache gems installed by bundler during the build stage instead of re-installing them each time the pipeline is executed. In recent Drone versions, caching is handled by Drone plugins which have to be used in specific pipeline stages. Here, we use the Drone plugin drillster/drone-volume-cache. This plugin allows to cache directories in Docker volumes on the Docker host. Note that for the plugin to be able to create volumes, the trusted flag has to be set in the project repository settings of Drone.

Adjusting Build Stage

The cache plugin is only able to cache directories residing in the Drone workspace directory which is /drone. However, the ruby image used in the build stage stores gems installed by bundler in /usr/local/bundle. Therefore, the BUNDLE_PATH environment variable has to be adjusted in the build stage such that bundler installs gems in a directory residing in /drone. For this, we choose the directory /drone/.bundle. The adjusted build stage looks as follows.

  build:
    image: ruby
    environment:
      - BUNDLE_PATH=/drone/.bundle
    commands:
      - bundle install
      - bundle exec jekyll build

Stage for Restoring the Cache

Before the build stage is executed, a stage is needed for restoring the cached directory out of the Docker volume. In the following, the configuration for this stage is shown where the cache plugin is configured accordingly. We define a volume by specifying a directory path on the Docker host, i.e. /tmp/bundle-cache, which is mounted in a directory path on the Docker container created during pipeline execution, i.e. /cache. The plugin is configured such that it copies the content of /cache to /drone/.bundle.

  restore-cache:
    image: drillster/drone-volume-cache
    restore: true
    mount:
      - /drone/.bundle
    volumes:
      - /tmp/bundle-cache:/cache

Stage for Rebuilding the Cache

After the build stage is executed, a stage is needed for rebuilding the cached directory in the Docker volume. In the following, the configuration for this stage is shown where the cache plugin is configured accordingly. We define the same volume as shown in the previous stage and configure the plugin such that it copies the content of /drone/.bundle back to /cache.

  rebuild-cache:
    image: drillster/drone-volume-cache
    rebuild: true
    mount:
      - /drone/.bundle
    volumes:
      - /tmp/bundle-cache:/cache

Final Pipeline Configuration

This is how the final pipeline configuration looks like.

pipeline:
  restore-cache:
    image: drillster/drone-volume-cache
    restore: true
    mount:
      - /drone/.bundle
    volumes:
      - jekyll-blog-cache:/cache
  build:
    image: ruby
    environment:
      - BUNDLE_PATH=/drone/.bundle
    commands:
      - bundle install
      - bundle exec jekyll build
  rebuild-cache:
    image: drillster/drone-volume-cache
    rebuild: true
    mount:
      - /drone/.bundle
    volumes:
      - jekyll-blog-cache:/cache
  deploy:
    image: drillster/drone-rsync
    hosts: [ "lafl-server.florian-latifi.at" ]
    user: blog
    source: _site
    target: /var/www/blog/
    recursive: true
    delete: true
    secrets: [ plugin_key ]
  notify:
    image: drillster/drone-email
    host: lafl-server.florian-latifi.at
    port: 25
    from: Drone <drone@lafl-server.florian-latifi.at>
    when:
      status: [ failure, success ]

branches: master

Conclusion

In this blog post I showed you how Drone can be used to automatically build and deploy a Jekyll site. We defined a pipeline configuration with stages for building the site, as well as deploying the generated files to a remote server. The pipeline configuration also contained a stage for notifying the developer via email when the pipeline execution succeeded or something failed during execution. Finally, to speed up builds we defined additional stages for caching the directory into which bundler installed gems are stored using a Docker volume.

Click here if you want to check out the source of my Jekyll blog where this pipeline configuration is also used.

I like Drone a lot, because it’s very easy to set up on a server, lightweight in resource usage and can be integrated with many different repository hosting software such as Github or Gitea. Furthermore, it’s highly extensible via plugins and the configuration of pipelines is also very simple. Therefore, I can highly recommend you to try it out.

I hope you liked this blog post. If you have any feedback or question regarding this topic, please leave a comment in the comments section below. Stay tuned for upcoming posts.