How I Make This Site

2nd Jan 2020

Built using Hugo

It’s extremely fast and straightforward, has great documentation, and is capable in most areas you’d need it to be (comes with SCSS and image compression support, webpack/ES6 can be integrated)

Using the Ezhil theme as a base

It’s a good theme, the code is clean, it has dark mode support.

I stripped out the feather.js since it’s slow and heavy, instead I checked out the Feather icons repo to the directly themes/feather/layouts/partials/, added "feather" to the themes array in config, and now I can use hugo partials to include the SVG files inline, like this:

{{ partial "icons/award.svg" }}

The icons render instantly since they’re inline to the page and no longer blocked on a DNS lookup

Two sites, one theme

Since this theme powers both this site and chillidonut.com, we want to be able to customise small aspects of each site. With hugo it’s easy to override certain partials. For example, when a “layout” file such as themes/2019/layouts/_default/index.html includes partial "footer.html", hugo will start looking in the project root for it, so we can override the theme’s footer file by creating a file named layouts/partials/footer.html.

In practise this allows us to include additonal scripts such as this page’s “flock” animation or chillidonut.com’s page transitions in the footer or header to give each site its own features and flair.

Deployment

My websites are built on a self-hosted service called Drone CI and hosted using Google Cloud Storage. Drone uses docker containers to make the building and pushing of the site files simple. I’ve included the drone config below, as it’s illustrative of a typical way you might do this with any CI service.

View .drone.yml
# .drone.yml
kind: pipeline
name: deploy

trigger:
  branch:
    - master

steps:
- name: submodules
  # we use submodules for the shared theme and the feather icons repo.
  # this checks out the code in a container with git available
  image: alpine/git
  commands:
  - git submodule init
  - git submodule update --recursive --remote
- name: hugo
  # build using hugo. this image also includes the bells and whistles
  # needed to compress images
  image: monachus/hugo:v0.58.3
  commands:
  - hugo --gc --minify
- name: upload
  # upload to public GCS bucket
  image: plugins/gcs
  settings:
    source: public/
    target: tommckenzie.dev/
    gzip: js,css,html
    cache_control: public,max-age=3600
    token:
      from_secret: deploy-token

Google Cloud Storage hosts the website

In Google Cloud Platform you want the “Storage” service, and to create a bucket which matches your domain exactly (i.e. tommckenzie.dev is the bucket name). You’ll have to verify you own the domain – I pointed my domain nameservers to cloudflare and added a DNS TXT record, which only took a few minutes.

Once you’ve created the bucket itself, from the bucket list view (of all your buckets) click the “dot-dot-dot menu”, select “Edit website configuration” and enter index.html and 404.html as the filenames. Don’t forget to do this, or the hosting won’t work and visitors will get an XML response instead of your website.

You’ll now want add a CNAME record pointing to your bucket — google explains how to do it well — but there are caveats which I’ll explain below.

a note on Google Cloud Storage, HTTPS, and CNAMEs

Since GCS doesn’t support HTTPS at all, you need to put something in front of it which does. I use cloudflare since a) they support adding CNAMEs to the apex domain (your domain without any subdomain), and b) they will handle HTTPS for you, for free.

Remember to set your site’s “SSL/TLS” setting to “Flexible”, and otherwise follow google’s instructions above.

redirecting www to your bare domain or vice versa (optional)

You only want one domain, usually the apex domain (yoursite.dev), but you might want www.yoursite.dev to redirect to this apex domain. Unfortunately, this is the trickiest part. You can probably use cloudflare workers to do it, or if you have a server with nginx or apache on it, you can use that, some people use lambda functions on AWS, I use a Kubernetes Ingress which simply redirects all www requests to the apex domain. Depending on your solution you may also need to set up HTTPS for it, too.

Again, if this is a new site, you might not need to bother. For chillidonut.com I have old links pointing to the www. domain so I want those links to return real redirect codes.

View www-chillidonut.ingress.yml
# just an ingress to redirect www.chillidonut.com traffic to chillidonut.com

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: www-chillidonut
  annotations:
    traefik.ingress.kubernetes.io/redirect-permanent: "true"
    traefik.ingress.kubernetes.io/redirect-regex: ^https?://www.chillidonut.com/(.*)
    traefik.ingress.kubernetes.io/redirect-replacement: https://chillidonut.com/$1
spec:
  rules:
  - host: www.chillidonut.com
    http:
      paths:
      - path: /
        backend:
          serviceName: default
          servicePort: 3000
  tls:
  - hosts:
    - www.chillidonut.com

such simple, much wow

This set up works great for me, especially the automated deployments, since I can edit or add content from any device, push it up via git, and have it live within minutes — but it does quickly get complex to set up. You can just as easily build with hugo on your machine and use gsutil cp . gs://mywebsite.com to deploy.

I hope you’ve found this helpful if you’re setting up a static website of your own!