Straightforward, minimal Docker Compose setup for Eleventy

Typical Docker use-case scenario: I was throwing together a blog with Eleventy, and the others did not have the required Node.js version for all functionality.

Eleventy doesn't require any fancy setup; I assume that's why there's no "official" or popular Docker image. The ones I quickly found looked half-backed or abandoned.

For this particular project, the CLI command for serving the site was:

npx @11ty/eleventy --input=*.njk --serve

Without any configuration file, as a one-line command, this for Docker would be:

docker run --rm -v ${PWD}:/app -w=/app node:16.15.0-alpine npx @11ty/eleventy --input=*.njk --serve

Eleventy when serving a website exposes a local and external URL:

[11ty] Watching…
[Browsersync] Access URLs:
 --------------------------------
    Local: http://localhost:8080
 External: http://172.17.0.2:8080
 --------------------------------
[Browsersync] Serving files from: _site

Because the process is running inside the container, use the external URL. There's no real need for doing a port mapping.

Now that macOS uses zsh by default, this chokes with the zsh: no matches found: --input=*.njk error; but works well with bash.

We can get around this entire error by adding the 11ty command in package.json, which we would do it anyway:

{
    "scripts": {
        "build": "npx @11ty/eleventy --input=*.njk",
        "serve": "npx @11ty/eleventy --input=*.njk --serve"
    }
}

This works well on Unbutu (bash) and macOS (zsh):

docker run --rm -v ${PWD}:/app -w=/app node:16.15.0-alpine npm run serve

I prefer having a Docker config file, not just because it shortens the commands, but IDEs pick up on the usage and provide support for it.

The docker-compose.yml can be as simple as:

version: '3'

services:
    site:
        image: node:16.15.0-alpine
        working_dir: /app
        volumes:
            - ./:/app

With this file in place, the command is now:

docker-compose run --rm site npm run serve

This is still too much typing for my taste, so I usually add a helper script, so I can run my command like:

./bin/docker npm run serve

The script (bin/docker) does nothing more than forward the arguments passed to it:

#!/bin/bash
CMD=$*

if [[ -z $CMD ]]; then
    echo "What command should we run inside the container? None provided."
    exit 1
fi

eval docker-compose run --rm site "$CMD"