Blogging with Eleventy

Bill Horsman on

Having posted sporadically on Medium I thought I'd finally put my thoughts into my own space. I moved the Logical Cobwebs website to Eleventy last week and while I'm on a roll…

Eleventy #

I started off with Eleventy Blog template which not only saves you time but it demonstrates a lot of techniques. There's some solid CSS included but my approach was to start from scratch and pull in bits that I wanted.

Lighthouse #

I love that, out of the box, the blog template includes the Lighthouse plugin which tests performance, accessibility, best practices and SEO. If you're not 100% on all those then the deploy fails! You can override that if you really want.

100% on all four metrics

Docker #

I wanted to be able to run this website easily on my Mac so I set up Docker.

This is my Dockerfile. Not much going on here:

  1. Use an image providing Node 18
  2. Copy over the app
  3. Install the packages
  4. Tell it to run on port 8082
FROM node:18.15-bullseye

RUN mkdir -p /app
WORKDIR /app

COPY . /app/
RUN npm install

EXPOSE 8082

CMD [ "npx", "@11ty/eleventy", "--serve", "--port=8082" ]

We're using Traefik as a load balancer which makes things easy (even though we don't really need it). Here's the docker-compose.yml file:

version: "3.7"

services:

lb:
image: traefik:v2.9
command:
- --providers.docker
ports:
- 80:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock

web:
build:
context: .
dockerfile: Dockerfile
labels:
- traefik.http.routers.whoami.rule=Host("dev.logicalcobwebs.com")"
depends_on:
- lb
container_name: logicalcobwebs
restart: always
volumes:
- ./:/app:cached
ports:
- 8082:8082

volumes:
logicalcobwebs-app:

And now it's just

docker-compose up web

And with dev.logicalcobwebs.com pointing to 127.0.0.1 I'm all set to go with it running at http://dev.logicalcobwebs.com (note: that website isn't available on the Internet, it's just on my machine).

Let's Encrypt! #

But we can do more! Let's get SSL working automatically by asking Let's Encrypt to generate the SSL certificate using a DNS challenge from DNSimple.

version: "3.7"

services:

lb:
image: traefik:v2.9
environment:
- DNSIMPLE_OAUTH_TOKEN
- DNSIMPLE_BASE_URL
command:
- --providers.docker
- --entrypoints.http.address=:80
- --entrypoints.https.address=:443
- --api.insecure=true
- --certificatesresolvers.letsencrypt.acme.dnsChallenge=true
- --certificatesresolvers.letsencrypt.acme.dnsChallenge.provider=dnsimple
- --certificatesresolvers.letsencrypt.acme.dnsChallenge.delayBeforeCheck=0
- --certificatesresolvers.letsencrypt.acme.email=${EMAIL}
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
labels:
- traefik.http.routers.to-https.rule=HostRegexp(`{host:.+}`)
- traefik.http.routers.to-https.entrypoints=http
- traefik.http.routers.to-https.middlewares=to-https
- traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)
- traefik.http.routers.traefik.entrypoints=https
- traefik.http.routers.traefik.service=api@internal
- traefik.http.routers.traefik.tls=true
- traefik.http.routers.traefik.tls.certresolver=${CERT_RESOLVER}
- traefik.http.middlewares.to-https.redirectscheme.scheme=https

ports:
- 80:80
- 8080:8080
- 443:443
volumes:
- ./data/letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock

web:
build:
context: .
dockerfile: Dockerfile
labels:
- traefik.http.routers.https.rule=Host(`${DOMAIN}`)
- traefik.http.routers.https.entrypoints=https
- traefik.http.routers.https.tls=true
- traefik.http.routers.https.tls.certresolver=${CERT_RESOLVER}
depends_on:
- lb
container_name: logicalcobwebs
restart: always
volumes:
- ./:/app:cached
ports:
- 8082:8082

volumes:
logicalcobwebs-app:

Finally, a .env file like this. You need to get DNSIMPLE_OAUTH_TOKEN from your DNSimple account settings, not from your user settings.

DOMAIN=dev.logicalcobwebs.com
EMAIL=
CERT_RESOLVER=letsencrypt
DNSIMPLE_BASE_URL=https://api.dnsimple.com
DNSIMPLE_OAUTH_TOKEN=

Now we can use https://dev.logicalcobwebs.com.