Setup HUGO using Docker
Tags: hugo, docker, webdev
Published: 2023-03-11 17:48:50

In this article I'm going to describe how to setup HUGO without installing a local version of HUGO on your machine by using  a Docker image instead.

When choosing a specific Docker image, be aware that HUGO comes in two editions, standard and extended. The extended edition supports Sass and encoding WebP images, it is recommended by the HUGO team to use the extended edition, and that's what we will do in this article.

We will be using a Docker image from this repository: [https://hub.docker.com/r/klakegg/hugo](https://hub.docker.com/r/klakegg/hugo). All Docker images containing `ext` in its name use the extended edition of HUGO. We will use the Alpine based image.

The Docker image contains the HUGO CLI and exposes the `hugo` command as the ENTRYPOINT. Running the container and specifying the arguments for the hugo command, gives you all the options you would have when you have HUGO installed locally.

For example, the following command would show the version of HUGO when HUGO is installed locally:

```bash
hugo version
```

The same command can be run using the Docker container:

```bash
docker run --rm klakegg/hugo:0.107.0-ext-alpine version
```

The --rm flag indicates the container is immediately disposed after finishing.


## NEW SITE
The following command will generate a new HUGO site called 'test':

```bash
docker run --rm -v $(pwd):/src klakegg/hugo:0.107.0-ext-alpine new site test --format yaml
```

A new folder 'test' will be created, which contains the generated site. The -v flag is used to create a volume from the present working directory (pwd). The `--format yaml` at the end enables yaml config files instead of toml.

To serve the site,  you first need to navigate to the root of the site:

```bash
cd test
```

Run the following command to serve the new site. (notice the added -p flag containing the port numbers):

```bash
docker run --rm -p 1313:1313 -v $(pwd):/src klakegg/hugo:0.107.0-ext-alpine server
```

The site is built in-memory and will be hosted at http://localhost:1313. 

You will see a 'Page not found' message when you visit the URL. Let's create an initial page by adding index.html to the layouts folder.

layouts/index.html
```html
<!doctype html> 
<html lang="en"> 
  <head> 
    <meta charset="utf-8"> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <title>HUGO & Docker demo</title>  
  </head> 
  <body> 
    <h1>Hello, world!!!</h1> 
  </body> 
</html>
```

You might need to restart the server to view the changes if your are working on Windows (I have a solution for that in a bit using docker-compose).

```bash
docker run --rm -p 1313:1313 -v $(pwd):/src klakegg/hugo:0.107.0-ext-alpine server
```

And now you will be greeted with 'Hello World' when you visit [http://localhost:1313](http://localhost:1313).


## DOCKER-COMPOSE
Now instead of running the server each time using `Docker run`, we can add the server to a docker-compose.yml file and spin up the server with `docker compose up`. File changes are automatically picked up:

docker-compose.yml
```yaml
services:
  server:
      image: klakegg/hugo:0.107.0-ext-alpine
      command: server -D --poll 700ms
      volumes:
        - ".:/src"
      ports:
        - "1313:1313"
```
The -D argument makes sure drafts are also build and served, this is what you usually want during development.
The `poll` flag is added for Windows users. In cases where Hugo CLI is running in a shared directory on a Linux VM on a Windows host the server is not detecting file changes from the host environment. The polling fixes this problem on Windows. More information [here](https://github.com/klakegg/docker-hugo/issues/61).


## EXPOSE HUGO CLI VIA PACKAGE.JSON
Now that we have a convenient way of serving the site, we need a better way to access the HUGO CLI instead of calling Docker run each time. We will create a package.json file for that, and use the scrips section to make the HUGO CLI more accessible.

Create a package.json:

```bash
npm init -y
```

Expose the HUGO CLI by adding the following command to the scripts section:

```json
"scripts": { 
    "hugo": "cross-env-shell docker run --rm -v $INIT_CWD:/src klakegg/hugo:0.107.0-ext-alpine" 
}
```

'cross-env-shell' is needed for a platform independent way of reading environments variables, such as the $INIT_CWD to get the current working directory.

```bash
npm i cross-env -D
```

Now you call the HUGO CLI like this:

```bash
npm run hugo -- --help
```

CLI arguments are placed after the `--`. 

To build the website you can use the hugo command without arguments.

```bash
npm run hugo
```

The `server` command as used earlier, builds the site in memory. The build command builds the site and stores the output in the ./public folder.

## CONCLUSION
Now you are able to get started with HUGO without installing it on your machine. Hope this helps someone, it took me a few hours to figure this out, especially to get it to work on Windows.