6.3 KiB
Hugo Docker CMS
Hugo-basiertes CMS im Docker-Stack mit Caddy als TLS-Reverse-Proxy.
Markdown-Dateien einfach in content/ ablegen – Hugo rendert sie automatisch.
Architektur
Port 5443
Browser ── HTTPS ──→ Caddy ── HTTP ──→ Hugo (live-reload)
:2-alpine hugomods/hugo:latest
self-signed markdown → HTML
| Komponente | Image | Aufgabe |
|---|---|---|
| Hugo | hugomods/hugo:latest (0.165+) |
Rendert .md-Dateien, live-reload via --watch |
| Caddy | caddy:2-alpine |
HTTPS-Terminierung, Reverse-Proxy → Hugo:1313 |
Verzeichnisstruktur
hugo_docker/
├── docker-compose.yml # Hugo + Caddy Stack
├── Caddyfile # TLS + Reverse-Proxy
├── scp_copy.py # SCP-Transfer + SSH Docker-Restart mit Health-Check
├── content/ # ← .md-Dateien hier ablegen (Host-Mount)
├── crt/ # Self-signed Zertifikate
├── site/ # Hugo-Config, Layouts, CSS
│ ├── hugo.toml
│ ├── entrypoint.sh # Auto-Frontmatter + Besitzer-Fix (chown) + Hugo-Server
│ ├── assets/css/main.css # Modernes CSS (oklch, dark mode, grid)
│ └── layouts/ # HTML-Templates
├── ansible/
│ ├── deploy.yml # Playbook
│ ├── tasks.yml # Geteilte Tasks (DRY)
│ ├── templates/ # Caddyfile.j2, hugo.toml.j2
│ └── molecule/default/ # CI-Tests (Ubuntu Docker)
└── .gitea/workflows/
└── deploy.yml # CI: Molecule-Test → Deploy
Schnellstart (lokal)
# Zertifikate generieren (nur einmal)
python3 -c "
import sys; sys.path.insert(0,'../Python_Skripte/create_selfsign')
from selfsign_ca import generate_and_save_cert
generate_and_save_cert(cert_common_name='localhost', cert_name='localhost')
"
# Container starten
docker compose up -d
# HTTPS aufrufen
curl -k https://localhost:5443/
Nutzung
# Neue Seite anlegen – .md-Datei in content/ werfen
cat > content/mein-artikel.md << 'EOF'
---
title: "Mein Artikel"
date: "2026-09-08"
---
Inhalt im **Markdown**-Format.
EOF
# Kein Neustart nötig – Hugo erkennt Änderungen automatisch
docker compose restart hugo # nur bei komplett neuen Dateien
SCP-Sync (scp_copy.py)
Skript zum Synchronisieren lokaler .md-Dateien auf den Ziel-Host und
Neustarten des Hugo-Containers via SSH:
cd hugo_docker
python3 scp_copy.py
Ablauf:
- SCP kopiert alle
.md-Dateien ausscr/nachdest docker compose restart hugowird via SSH auf dem Ziel-Host ausgeführt- Health-Check pollt
curl https://localhost:5443/bis Hugo200antwortet (max 15 s)
Alle Pfade sind dynamisch aus dest abgeleitet — compose_file wird aus dem
content/-Pfad berechnet (content/ → docker-compose.yml). Keine hartkodierten Pfade.
Frontmatter
Der Entrypoint (site/entrypoint.sh) fügt automatisch fehlendes YAML-Frontmatter
in .md-Dateien ein – extrahiert Titel aus der ersten #-Überschrift, Datum aus
dem Datei-Änderungsdatum.
Hugo benötigt diesen ----Block:
---
title: "Meine Seite"
date: "2026-09-08"
---
Mit dem Entrypoint kannst du beliebige .md-Dateien in content/ werfen.
Permission-Handling
Der Hugo-Container läuft als root (notwendig für den Build-Lock).
Der Entrypoint gibt den Besitz modifizierter .md-Dateien per chown an den
Host-User zurück — gesteuert über HOST_UID/HOST_GID in docker-compose.yml:
environment:
HOST_UID: "1000"
HOST_GID: "1000"
Kein sudo chown-Gefrickel mehr — SCP-Overwrites klappen nach jedem Restart.
Deployment (Ansible)
Auf entfernten Host ausrollen:
cd ansible
ansible-playbook -i "ras-dan-01," deploy.yml
Variablen (optional):
ansible-playbook -i "ras-dan-01," deploy.yml \
-e "target_hostname=ras-dan-01 target_port=5443"
Das Playbook legt alles unter ~/.docker/hugo/ auf dem Ziel-Host ab,
rendert Templates mit Hostname/Port und startet die Container.
Zertifikate
Generiert mit Python_Skripte/create_selfsign/selfsign_ca.py (lokales Tool).
Jedes Zertifikat enthält eine SAN (Subject Alternative Name) Extension –
Pflicht für moderne Browser.
# CA systemweit vertrauen (optional, für Browser ohne Warnung)
sudo cp crt/ca.crt /etc/ca-certificates/trust-source/anchors/hugo-docker-ca.crt
sudo update-ca-trust
# Firefox / qutebrowser (NSS-Store)
certutil -A -d sql:$HOME/.pki/nssdb/ -t "CT,," \
-n "Hugo Docker CA" -i crt/ca.crt
CI/CD (Gitea Actions)
Workflow in .gitea/workflows/deploy.yml – läuft auf Runner mit Label shell:
Push auf main
→ molecule test (Ubuntu Docker)
→ syntax, create, prepare, converge, idempotence, verify, destroy
→ ansible-playbook deploy auf ras-dan-01
Voraussetzungen auf dem Runner:
- Docker + Docker Compose
- molecule + molecule-plugins[docker] (vorinstalliert)
- ansible-core (vorinstalliert)
- SSH-Agent mit Key für ras-dan-01
CSS-Features
Modernes Theme in site/assets/css/main.css:
oklch()-Farbraum mitlight-dark()– automatisch hell/dunkel- CSS Grid Layout (Header – Main – Footer)
- TOC (Table of Contents) als aufklappbares
<details>-Element - Code-Highlighting mit Catppuccin-Mocha-Theme
- Responsive (max-width: 840px)
Hugo-Version
- Image:
hugomods/hugo:latest(aktuell 0.165.0, August 2026) hugo.tomlverwendetlocalestattlanguageCode(v0.158+)- Goldmark-Renderer mit
unsafe: true(HTML in Markdown erlaubt)
Troubleshooting
| Problem | Lösung |
|---|---|
| Neue .md-Dateien erscheinen nicht | docker compose restart hugo (Entrypoint läuft nur beim Start) |
| Browser zeigt Zertifikatswarnung | CA in NSS-Store importieren (s.o.) |
| Caddy startet nicht | docker compose logs caddy – TLS-Zertifikate prüfen unter crt/ |
| Zertifikatsfehler "no SAN" | Zertifikate mit selfsign_ca.py neu generieren (SAN ist jetzt enthalten) |
| Molecule-Test schlägt fehl | ansible_remote_tmp: /tmp/.ansible in molecule.yml prüfen |
| 502 Bad Gateway nach Deploy | ssh <host> "docker compose -f ~/.docker/hugo/docker-compose.yml logs hugo" |
| Permission denied bei SCP | HOST_UID/HOST_GID in docker-compose.yml prüfen, ggf. chown manuell ausführen |