Serverless Hosting Of A Static Page With Jekyll, CircleCI, Amazon AWS S3 And Cloudfront

As static page hosting becomes more and more popular, there should be an easy way to host your site without the hassle of keeping a full server running somewhere. For hosting some client projects and this blog, I built a pipeline with Github, CircleCI and Amazon AWS to get the static content online without the need to manage the full stack of a server including operating system, system updates and webserver config. With this how-to you can update your website with a simple git push to your underlying repository and the site will be built and updated automatically. The monthly costs for a low traffic blog will be around less than a dollar.

To get your blog running like this, do the following steps.

Origin Domain Name: yourdomain.com.s3-website.<your-region>.amazonaws.com (DO NOT use what amazon offers you here as autosuggest)

Alternate Domain Names (CNAMEs): yourdomain.com

Default Root Object: index.html

Compress Objects Automatically: Yes

I would recommend to leave everything else as it is and create the Cloudfront distribution. You can change all settings later but make sure before everything is running smoothly.

{
  "Statement": [
    {
      "Action": [
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:PutObject",
        "s3:PutObjectAcl"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::yourdomain.com/*"
      ]
    },
    {
      "Action": [
        "s3:ListBucket"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Action": [
        "cloudfront:CreateInvalidation"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

ATTENTION: AWS doesn't support resource identifiers for Cloudfront in IAM policies, which means pratically any user with this permission can invalidate all your distributions.

machine:
  python:
    version: pypy-2.2.1
  environment:
    JEKYLL_ENV: production

dependencies:
  pre:
    - pip install awscli

compile:
  override:
    - echo "disabled"

test:
  override:
    - bundle exec jekyll build

deployment:
  production:
    branch: master
    commands:
      - |
        aws --version
      - |
        aws s3 sync --region your-region --acl public-read --delete _site/ s3://yourdomain.com
      - |
        echo -e "\n[preview]\ncloudfront = true" >> ~/.aws/config
      - |
        cat ~/.aws/config
      - |
        aws cloudfront create-invalidation --cli-input-json '{"DistributionId":"'"$AWS_CLOUDFRONT_ID"'","InvalidationBatch":{"Paths":{"Quantity":2,"Items":["/index.html","/*"]},"CallerReference": "'$(date "+%s")'"}}'

Awesome! Now you are running a super fast website and you don't need to care about anything then producing awesome content!

Some trade offs of this solution: