Deploying Hugo with Docker on a VPS
hugo
docker
nginx
How to build a Hugo site into a multi-stage image and serve it with Nginx, without shipping the compiler inside the final image.
This is a sample post you can delete. It’s here to check that syntax highlighting, tables, and the table of contents all render properly.
Why multi-stage
Hugo generates static HTML. Once the site is compiled, the Hugo binary is no longer needed. With a two-stage build, the final image contains only Nginx and the generated files:
FROM hugomods/hugo:exts-0.154.5 AS builder
WORKDIR /src
COPY . .
RUN hugo --minify --gc
FROM nginx:alpine
COPY --from=builder /src/public /usr/share/nginx/htmlThe size difference is substantial:
| Approach | Approximate size |
|---|---|
| Image with Hugo included | ~450 MB |
| Multi-stage with Nginx | ~55 MB |
The baseURL catch
Hugo writes the baseURL into the HTML at build time. If you compile with the default value and then serve from a different domain, the stylesheets point to the wrong place. That’s why it’s worth passing it as a build argument:
docker compose build --build-arg HUGO_BASEURL="https://yourdomain.com/"Handy commands
# Write with live reload
docker compose -f compose.dev.yaml up
# Publish
docker compose up -d --buildIf the site renders without styles after deploying, it’s almost always the
baseURL. Check the<link rel="stylesheet">in the served HTML.