I have been running a Gradle & Groovy related blog for an about a year using Wordpress. Wordpress is a great platform, but comes with its own risks and drawbacks:

  • More maintenance (needs a server to run)
  • Prone to hacking
  • Slower than static blog
  • Higher operational costs

I’ve decided to host a simple, static blog using S3 static site feature. I’ve also have chosen to use Jekyll as an engine to help me keep my static website maintainable.

Installing Jekyll on macOS

gem install jekyll bundler
cd ~/blog/deploy.live
jekyll new deploy.live
cd deploy.live
jekyll serve

You can now view your blog engine running by accessing http://localhost:4000.


  • Personalise _config.yml for your own blog.
  • Append _config.yml with destination: _deploy
  • Add a new blog post by creating a file in the _posts directory that follows the convention YYYY-MM-DD-name-of-post.ext.

Configure AWS S3

Configure S3 Bucket

  • Log-in to your AWS account and open S3.
  • Click Create bucket
  • Provide DNS compatible bucket name, so for me it was deploy.live
  • Select the region you are expecting the most traffic in
  • Click Next
  • I will add tags for this bucket to be able to track my S3 cost for this website
  • Click Next
  • Grant Global Read permissions on the bucket. Manage public permissions > Everyone > Objects > Read
  • Click Next and Create a Bucket.

Apply S3 Bucket policy

  • Generate Bucket policy using this URL http://awspolicygen.s3.amazonaws.com/policygen.html
  • Select S3 Bucket Policy
  • Select Effect Allow
  • Select Pricinpal *
  • Select AWS Service Amason S3
  • Select Actions GetObject
  • Specify ARN as per Amazon S3 > Bucket Name > Permissions > Bucket Policy. e.g. arn:aws:s3:::deploy.live, append it with /* e.g. arn:aws:s3:::deploy.live/*
  • Generate Policy
  • Apply Policy to Bucket Policy
        "Version": "2012-10-17",
        "Id": "Policy1490566913841",
        "Statement": [
                "Sid": "Stmt1490566176363",
                "Effect": "Allow",
                "Principal": "*",
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::deploy.live/*"

Configure IAM for Ansible API access

  • Open IAM
  • Open Users tab
  • Click Add User
  • Specify user name e.g. ansible_s3
  • Select Access Type as Programatic access
  • Click Permissions
  • Select Attach existing policies directly
  • Select AmazonS3FullAccess
  • Click Review
  • Click Create user
  • Download credentials as .csv

Configure AWS CLI

pip install --upgrade --user awscli
aws configure
AWS Access Key ID [None]: <Enter Access Key ID here>
AWS Secret Access Key [None]: <Enter Secret Access Key here>
Default region name [ec-west-2]: eu-west-2 (I am hosting in London S3 Region)
Default output format [None]: text

Write Ansible deploy script

Prerequisite: installed ansible

I am strong believer in ansible roles, therefore even for the most simpliest playbooks, I will be creating a distinct roles. For this very project I will have the following structure:

  • roles
    • deploy-jekyll-s3
      • defaults
        • main.yml (variables specific to your bucket and jekyll directory)
      • tasks
        • main.yml (generic build and deploy script)
  • main.yml

where roles/deploy-jekyll-s3/defaults/main.yml is as follows:

jekyll_blog_root_dir: /Users/dmitrilerko/blog/deploy.live/deploy.live
bucket_name: deploy.live

and roles/deploy-jekyll-s3/tasks/main.yml builds and deploys S3 using AWS CLI:


- name: build jekyll
  command: jekyll build
    chdir: "{{ jekyll_blog_root_dir }}"

- name: upload blog to S3
  command: "aws s3 cp {{ jekyll_blog_root_dir }}/_deploy/ s3://{{ bucket_name }}/ --recursive"

Full ansible script can be found here: https://github.com/dmitri-lerko/ansible-jekyll.

Now lets run ansible playbook.

ansible-playbook main.yml
PLAY [upload deploy.live to S3] ************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [deploy-live-s3 : build jekyll] *******************************************
changed: [localhost]

TASK [deploy-live-s3 : upload blog to S3] **************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0

Test your brand new static website http://deploy.live.s3-website.eu-west-2.amazonaws.com/