OK, let’s do this.

There are a lot of blog posts describing how to create all Amazon AWS resources to host a static website like the one created by Hugo. Since, I have a DevOps background, I felt this urge to automate this process. In my professional life, I use Ansible … a lot. It should not come as a surprise that I picked this tool to automate all the things.


Domain Name

You also need a domain name. If you haven’t got one, register one, either via Amazon AWS Route53 or some other Domain Name Registrar (I chose TransIP).

Update nameservers (optional)

If you used Route53 to register your domain, you’re done: the domain is already added as a Public Hosted Zone in Route53 and you’re ready to go.

If you’re using a different registrar, create a Public Hosted Zone and write down the values of the NS record (see image).


You need those values to update the nameservers with your domain. As an example, here’s what I had to change with my registrar (~ excuse me for being dutch ;))


After you updated those nameserver settings, wait for a bit, and all DNS queries will be routed to Route53. This means you can create DNS records in Route53. As I said, this step is totally optional, but now you got an API! This essentially means you can create DNS records using tools like Terraform, Ansible, Chef, Puppet, and what not.

Create Amazon AWS resources

Let’s start with a simplified overview. Here’s what we’re going create:


That doesn’t seem that complicated, now does it? Also notice that no compute instance (like EC2) is used. This means it’s compatible with the 2016 buzzword: serverless!!1!


  • S3 Bucket: will contains all generated and static content (html, css, js and images).
  • Amazon CloudFront: this Content Delivery Network (CDN) service replicates all files across the globe so users experience the best possible speed.
  • Amazon Route 53: the Domain Name Service (DNS) that translates an easy to remember DNS record to our CloudFront distribution.
  • TLS/SSL certificate: we’ll use the Amazon Certificate Manager (ACM) to request a TLS/SSL certificate to encrypt all traffic to and from our website. (~ ah, … I forgot to include this one in the overview … please forgive me)

After all resources are created and aligned, world domination awaits. Endusers around the world will be able to access your website.

Let’s start with my project directory:

 ├── ansible
 │   ├── roles
 │   │   ├── hugo-aws-infra
 │   │   ├── hugo-s3-deploy
 ├── ansible.cfg
 ├── Makefile
 ├── site.yml
 ├── hugo
     ├── content
     │   ├── ... <hugo content>
     ├── static
     │   ├── ... <static stuff>
     ├── themes
     │   ├── ... <hugo themes>
     |-- config.toml

The hugo directory contains my website, created with the command hugo new site whatever. I picked a theme from http://themes.gohugo.io and created content (~ you’re reading it).

Notice that I’ve downloaded the Ansible roles from Github to the ansible/roles directory (for Github links see bottom of this post).

I’m using this simple Makefile to start it all:


ifneq (,$(findstring debug,$(MAKECMDGOALS)))

ifneq ($(tags),)
$(eval TAGS:=--tags '$(tags)')

ifneq ($(skip),)
$(eval SKIP_TAGS:=--skip-tags '$(skip)')

ANSIBLE=ansible-playbook $(ANSIBLE_DEBUG) $(TAGS) $(SKIP_TAGS)

	@printf "\nUsage: make site [OPTIONS]\n";

	@# dummy target to prevent the msg "No rule to make target 'debug'"

		-i localhost \
		-e BASE_PATH=`pwd` \
		--extra-vars "$(MAKEFLAGS)" \

… and this Ansible playbook is executed.

  - name: Create Hugo infra on Amazon AWS
    hosts: localhost
    gather_facts: true

      website_domain: foobar-it.com
      verbose_output: true

      - role: hugo-aws-infra
        s3_website_domain: "{{ website_domain }}"
        verbose: "{{ verbose_output }}"
          - infra

      - role: hugo-s3-deploy
        hugo_base_path: ../hugo
        s3_website_domain: "{{ website_domain }}"
        verbose: "{{ verbose_output }}"
          - deploy

Notice the tags; use them to restrict the either the creation of all AWS infra or deploying your static website to S3. See the following examples:

# do all the things
$ make site

# only infra
$ make site tags=infra

# only the s3 part within infra ... in `debug` mode
$ make site tags=s3 debug

# push static site to s3 in `debug` mode, ... with `verbose` mode on
$ make site tags=deploy debug verbose=true

The possibilities are endless, … use it as you see fit.

Final note

I’ve shown you the quick route to deploying your static website to Amazon AWS. I haven’t talked about how I actually created all the resources. You could have a look yourself, but in a next post, I’ll highlight a couple of parts which I think deserve some more attention. Also, I’ll show you how to setup a CI/CD pipeline using Jenkins.

And, as promised, here are the Github links for the Ansible roles I created:

~ enjoy!