Hugo Doks Academic Website on Synology NAS

March 12, 2026 5 minutes

Date: 2026-03-12 Status: Complete — container running at http://192.168.1.70:1212/


Overview

Built and deployed a personal academic website on Synology NAS using Docker. The site uses Hugo with the Doks theme and auto-rebuilds whenever Markdown files are dropped into /var/services/homes/omnihola/website-content/.


Architecture

/var/services/homes/omnihola/website-content/              ← intellectual property (your .md files)
├── about/
├── publications/
├── blog/
├── projects/
└── other/

/volume1/docker/hugo-doks/      ← Hugo engine & Docker infrastructure
├── site/                       ← Hugo project root
│   ├── config/_default/
│   ├── assets/scss/
│   ├── layouts/partials/
│   └── content/en/             ← scaffold only; /var/services/homes/omnihola/website-content mounted here at runtime
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
├── supervisord.conf
└── scripts/watch-and-build.sh

Runtime flow:

Drop .md file → /var/services/homes/omnihola/website-content/
       ↓
inotifywait detects change (~2s debounce)
       ↓
hugo --minify rebuilds site (~3-8s)
       ↓
nginx serves /site/public/ on port 80
       ↓
Synology reverse proxy (HTTPS port 443) → public internet

Tech Stack

ComponentVersion / Detail
HugoExtended 0.128.0
Theme@hyas/doks-core v1.6.1 (npm)
Node.js20 Alpine (builder stage only)
NginxAlpine package
SupervisordAlpine package
inotify-toolsAlpine package
Docker basenode:20-alpine (builder) + alpine:3.20 (runtime)
Docker Composev2.20.1

Site Sections

SectionURLContent path
About / Bio/about//var/services/homes/omnihola/website-content/about/
Publications/publications//var/services/homes/omnihola/website-content/publications/
Blog / Notes/blog//var/services/homes/omnihola/website-content/blog/
Projects/projects//var/services/homes/omnihola/website-content/projects/
Other/other//var/services/homes/omnihola/website-content/other/

Key Files

docker-compose.yml

services:
  hugo-doks:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: hugo-doks
    ports:
      - "1212:80"
    volumes:
      - /volume1/homes/omnihola/website-content:/site/content-raw/en:ro
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Dockerfile (multi-stage)

  • Stage 1 (builder): node:20-alpine + Hugo + npm install + hugo --minify
  • Stage 2 (runtime): alpine:3.20 + nginx + inotify-tools + supervisord + Hugo + nodejs
  • gcompat required in both stages (Hugo Extended is glibc-linked, Alpine uses musl)
  • nodejs required in runtime stage (PostCSS transform needs Node at hugo rebuild time)

module.toml (critical — how Doks theme mounts)

The Doks v1.x theme uses npm packages resolved via Hugo module mounts (not Go modules). site/config/_default/module.toml mounts all theme files from node_modules/ and maps content/en directly as the Hugo content directory.

watch-and-build.sh

  • Watches /site/content recursively with inotifywait
  • 2-second debounce to batch rapid multi-file uploads
  • Runs hugo --minify on change
  • Logs [watcher] and [hugo] prefixed output
  • Build failures are logged but don’t crash the watcher

Issues Encountered During Build

1. Hugo Extended fails on Alpine (glibc issue)

Hugo Extended binary is dynamically linked against glibc. Alpine uses musl libc. Fix: Add gcompat to both builder and runtime stages in Dockerfile.

2. Wrong package version — @hyas/doks-core@^2.4.2 does not exist

The package never reached v2. Latest is v1.6.1. Fix: Updated package.json to @hyas/[email protected] with all required companions:

{
  "dependencies": {
    "@hyas/doks-core": "^1.6.1",
    "@hyas/images": "^3.2.0",
    "@hyas/seo": "^2.3.0",
    "@hyas/inline-svg": "^1.1.0",
    "@tabler/icons": "^3.3.0",
    "gethyas": "^2.4.2"
  }
}

3. Hugo module system wrong

Original hugo.toml used [[module.imports]] (Go module system). Doks uses npm packages resolved via Hugo module mounts. Fix: Removed [module] block from hugo.toml. Created site/config/_default/module.toml that mounts all theme files from node_modules/.

4. Missing layout partials required by Doks

Fix: Created:

  • layouts/partials/footer/script-footer-custom.html
  • layouts/partials/head/custom-head.html
  • layouts/partials/head/script-header.html

5. Missing SCSS files

Fix: Created:

  • assets/scss/common/_variables-custom.scss
  • assets/scss/common/_custom.scss

6. PostCSS config missing

Fix: Created site/config/postcss.config.js. Added @fullhuman/postcss-purgecss to devDependencies.

7. Node.js needed at runtime

PostCSS transform requires Node during hugo rebuild (not just build time). Fix: Added nodejs to the runtime Alpine stage in Dockerfile.

8. rsync blocked on NAS

Synology SSH uses internal-sftp subsystem — rsync is blocked. Fix: Used tar | ssh pipe to transfer files:

tar -czf - /local/files/ | ssh nas "cd /var/services/homes/omnihola/website-content && tar -xzf -"

9. Content directory moved to home/Drive

The content directory now lives under the Synology home folder: /var/services/homes/omnihola/website-content.

This path is visible through Synology Drive/File Station and is mounted read-only into the container as /site/content-raw/en.


NAS-Specific Notes

ItemDetail
Docker binary path/volume1/@appstore/ContainerManager/usr/bin/docker (NOT in PATH)
Docker Composedocker compose (v2, with space) works
rsyncBlocked — use tar | ssh pipe instead
Content directory/var/services/homes/omnihola/website-content (Drive-friendly home folder)
NAS IP192.168.1.70
SSH useromnihola
Container port1212 (host) → 80 (container)

Content Workflow

Adding a new publication

  1. Create a .md file:
---
title: "Paper Title"
date: 2026-03-12
authors: ["Your Name", "Co-author"]
journal: "Nature"
doi: "10.xxxx/xxxxx"
draft: false
---
Abstract here.
  1. Upload to /var/services/homes/omnihola/website-content/publications/ via Synology Drive/File Station or SFTP
  2. Site auto-rebuilds within ~8 seconds — no manual steps needed

Uploading files via terminal

scp mypaper.md nas:/var/services/homes/omnihola/website-content/publications/mypaper.md

Operations Reference

View container logs

ssh nas \
  "sudo /volume1/@appstore/ContainerManager/usr/bin/docker logs hugo-doks --tail 50 -f"

Restart container

ssh nas \
  "cd /volume1/docker/hugo-doks && sudo /volume1/@appstore/ContainerManager/usr/bin/docker compose restart"

Rebuild after config changes

# Transfer updated files
tar -czf - -C /home/benny/Desktop/claude/hugo-doks-project . \
  --exclude './site/node_modules' --exclude './site/public' | \
  ssh nas \
  "cd /volume1/docker/hugo-doks && tar -xzf -"

# Rebuild image and restart
ssh nas \
  "cd /volume1/docker/hugo-doks && \
   sudo /volume1/@appstore/ContainerManager/usr/bin/docker build -t hugo-doks:latest . && \
   sudo /volume1/@appstore/ContainerManager/usr/bin/docker compose up -d"

TODO

  • Customize name — edit /volume1/docker/hugo-doks/site/config/_default/params.toml, replace "Your Name" with actual name, then rebuild image
  • Configure reverse proxy — DSM → Control Panel → Application Portal → Reverse Proxy → point domain to localhost:1212
  • SSL certificate — DSM → Control Panel → Security → Certificate → Let’s Encrypt
  • Add favicon — add assets/favicon.png (512×512px minimum) to silence build warning
  • Future migration — @hyas/* packages are deprecated in favour of @thulite/*. Low priority since everything works.

Local Project Files

Source files on local machine: /home/benny/Desktop/claude/hugo-doks-project/ Design spec: /home/benny/Desktop/claude/docs/superpowers/specs/2026-03-12-hugo-doks-nas-design.md Implementation plan: /home/benny/Desktop/claude/docs/superpowers/plans/2026-03-12-hugo-doks-nas.md