Before you begin

In this tutorial, we’ll learn how to deploy new Jekyll posts on a remote server. In order to do that we need the following requirements:

  • Docker
  • Git
  • Bitbucket account
  • Remote server with a webserver installed (e.g., Nginx or Apache)

Create a Bitbucket repository with pipelines enabled

This can be achieved by creating a repository on Bitbucket and enabling Pipelines from the repository’s sidebar on the left there should be a pipelines page where there is a button Enable Pipelines. This will create a file called bitbucket-pipelines.yml at a root level of the repository with the following content:

pipelines:
  default:
    - step:
        script:
          - echo "Everything is awesome!"

Enable Bitbucket to SSH the remote server

Create a SSH keys going to SSH keys section of the repository under Settings / Pipelines. Add the generated public key to the ~/.ssh/authorized_keys of the remote server.

Finally, being on the SSH keys page add the remote server to the Known host by providing it the IP, clicking fetch and finally Add host.

Use Jekyll docker image to create a new blog

As an alternative of installing Jekyll in the host machine we can use Docker by creating an alias called jekyll that is running a Jekyll Docker container running a specific command and sharing a volume between the container and the host.

$ alias jekyll='docker run -it --rm --volume=`pwd`:/srv/jekyll -p 4000:4000 jekyll/jekyll jekyll'

Previous alias can be added into the .bashrc, ‘bash_profile, .zshrc or any startup terminal script. Once the jekyll command is available we can create a new blog running:

$ jekyll new blog

Edit bitbucket-pipelines.yml file

Replace existing bitbucket-pipelines.yml file with the following content:

image: jekyll/jekyll

pipelines:
  default:
    - step:
        script:
          - apk --no-cache add rsync openssh
          - cd blog && jekyll build && cd ..
          - rsync -avz blog/_site/ [user]@[remote server]:/var/www/html/
  • image: jekyll/jekyll Runs a jekyll docker image.
  • apk --no-cache add rsync openssh Installs rsync and its dependencies
  • cd blog && jekyll build && cd .. Moves to the blog application folder and builds the blog.
  • rsync -avz blog/_site/ [user]@[remote server]:/var/www/html/`: Copies the static generated files to the remote server.

jekyll build generates HTML files from _posts markdown files and puts them on the _site folder. More information about Jekyll here: jekyll-docs

To use a specific environment value in the build like JEKYLL_ENV=production we can do in Bitbucket by going to Environment variable section under Settings / Pipelines.

Finally, use git push to push commits made on your local branch to a remote repository and check on the pipelines section if last pipeline status is Successful. http://REMOTE_SERVER_IP should display the main page of the blog.

Bonus track: Ansible to deploy static websites

In previous example we’ve deployed a jekyll blog using bitbucket pipelines and rsync. In this quick example we are going to use Ansible instead of rsync to move files between machines. This is the bitbucket-pipelines.yml file:

image: jekyll/jekyll

pipelines:
  default:
    - step:
        caches:
          - bundler
        script:
          - apk --no-cache add tree rsync openssh ansible
          - cd blog && bundle install --path ../vendor/bundle && JEKYLL_ENV=production jekyll build && cd ..
          - ansible-playbook -i inventory/hosts deploy.yml

definitions:
  caches:
    bundler: vendor/bundle

As you can see we’ve replaced rsync command for Ansible and added caches sub-step.

Bitbucket Pipelines allow you to save dependencies in a folder and use them in the next build. In this example using caches reduced 20 seconds on each build).

Apart from bitbucket-pipelines.yml we need a hosts file (place to define remote machine/s) and a deploy.yml (known as a playbook and a place to define what task/roles you can execute using Ansible).

inventory/hosts:

[production]
remote_server_ip_or_hostname

deploy.yml:

---
- hosts: production
  tasks:
    - name: OS basic apt
      apt:
        name: python
        update_cache: yes
    - name: Rsync task
      synchronize:
        src: blog/_site/
        dest: /var/www/html/

In previous playbook we’ve defined what hosts we are going to apply a list of tasks and a description of those tasks:

OS basic apt - We are ensuring that the remote machine has python installed (needed by Ansible).

Rsync task - We are using an Ansible module called synchronize to move files from local to remote host.