↓ Skip to main content

Containerisierte Hugo Website: Ein Leitfaden für Docker und CI/CD

·1026 words·5 mins

Containerisierte Hugo Website: Ein praktischer Leitfaden
#

Hugo ist einer der beliebtesten statischen Site-Generatoren – schnell, flexibel und einfach zu bedienen. Doch wie bringst du deine Hugo-Website in einen Container und automatisierst die Bereitstellung? In diesem Beitrag zeige ich dir, wie du Docker, Docker Compose und CI/CD für dein Hugo-Projekt einrichtest.

Warum Containerisierung?
#

Container bieten mehrere Vorteile für statische Websites wie Hugo:

  • Konsistente Umgebungen – Entwicklung, Test und Produktion laufen in identischen Umgebungen
  • Einfache Bereitstellung – Ein Container kann überall laufen: lokal, auf einem Server oder in der Cloud
  • Isolation – Abhängigkeiten deiner Website beeinflussen andere Anwendungen nicht
  • Portabilität – Teile dein Setup mit anderen Entwicklern oder der Community
  • CI/CD-Integration – Automatisiere Build, Test und Deployment

Schritt 1: Dockerfile für Hugo
#

Ein gutes Dockerfile für Hugo sollte mehrere Ziele verfolgen:

  • Sicherheit – Nicht als Root laufen
  • Effizienz – Kleine Images durch Multi-Stage Builds
  • Wartbarkeit – Klare Trennung von Build- und Runtime-Umgebung

Multi-Stage Build
#

Ein bewährtes Muster ist der Multi-Stage Build: Ein Stage für den Build-Prozess (mit allen notwendigen Tools), ein Stage für die Runtime (minimal und sicher).

# Stage 1: Builder
FROM golang:1.26-alpine AS builder

# Hugo herunterladen und entpacken
ARG HUGO_VERSION="0.161.1"
RUN curl -sL \
  "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz" \
  -o hugo.tar.gz && \
  tar -xzf hugo.tar.gz hugo && \
  rm hugo.tar.gz

# Projekt kopieren und Site bauen
WORKDIR /app
COPY . .
RUN ./hugo --minify --destination /app/public

# Stage 2: Runtime mit Nginx
FROM alpine:3.23

# Nginx installieren
RUN apk add --no-cache nginx ca-certificates curl

# Nicht-Root-Benutzer erstellen
RUN addgroup -g 1000 app && \
    adduser -D -u 1000 -G app -s /sbin/nologin app

# Statische Dateien kopieren
WORKDIR /opt/app
COPY --from=builder --chown=1000:1000 /app/public .

# Berechtigungen setzen
RUN find /opt/app -type d -exec chmod 755 {} \; && \
    find /opt/app -type f -exec chmod 644 {} \;

USER 1000

EXPOSE 8080
ENTRYPOINT ["nginx", "-g", "daemon off;"]

Wichtige Punkte im Dockerfile
#

  • Alpine Linux als Basisimage für minimale Größe
  • Hugo Extended Version für Full-Feature-Support (z.B. für SASS/SCSS)
  • Nginx als effizienter Webserver für statische Inhalte
  • Nicht-Root-Benutzer für mehr Sicherheit
  • Minify-Flag für optimierte Ausgabedateien

Theme-Submodules
#

Wenn du ein Hugo-Theme als Git-Submodule verwendest, solltest du sicherstellen, dass es im Container verfügbar ist:

# im Builder-Stage
RUN if [ ! -d themes/dein-theme ]; then \
      git clone --depth 1 https://github.com/nutzname/dein-theme.git themes/dein-theme; \
    fi

Oder besser: Initialisiere die Submodules lokal und kopiere sie mit in den Container.

Schritt 2: Docker Compose für lokale Entwicklung
#

Mit Docker Compose kannst du deine containerisierte Hugo-Website lokal starten und testen.

version: '3.9'

services:
  hugo:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        APP_VERSION: "dev"
    image: deine-hugo-site:latest
    container_name: hugo-web
    ports:
      - "8080:8080"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 5s
    restart: unless-stopped

Vorteile von Docker Compose
#

  • Einfache Verwaltung – Starten, Stoppen und Neubauen mit einem Befehl
  • Healthchecks – Automatische Überprüfung, ob der Container läuft
  • Automatischer Neustart – Bei Abstürzen wird der Container automatisch neu gestartet
  • Port-Mapping – Lokale Zugriff auf den Webserver

Schritt 3: CI/CD mit GitLab CI
#

Eine CI/CD-Pipeline automatisiert den Build- und Deployment-Prozess. Hier ein Grundgerüst für GitLab CI:

variables:
  APP_NAME: 'hugo-site'
  APP_PORT: '1313'

stages:
  - build
  - deploy

build:container:
  image:
    name: moby/buildkit:rootless
    entrypoint: [""]
  stage: build
  variables:
    GIT_SUBMODULE_STRATEGY: recursive
  script:
    - buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --output type=image,name=$CI_REGISTRY_IMAGE:$APP_VERSION,push=true
  artifacts:
    reports:
      dotenv: .env

deploy:container:
  stage: deploy
  needs:
    - job: build:container
      artifacts: true
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker pull $IMAGE
    - docker stop $APP_NAME || true
    - docker rm $APP_NAME || true
    - docker run -d \
        --name $APP_NAME \
        --restart unless-stopped \
        -p $APP_PORT:8080 \
        $IMAGE

Wichtige CI/CD-Konzepte
#

  • Build-Stage – Erstellt das Docker-Image mit BuildKit
  • Cache-Management – Nutzt Registry-Caches für schnellere Builds
  • Deploy-Stage – Zieht das Image und startet den Container
  • Variablen – Zentrale Konfiguration für Image-Namen, Ports etc.

BuildKit für effiziente Builds
#

BuildKit bietet mehrere Vorteile:

  • Cache-Mounts – Beschleunigen wiederholte Builds
  • Parallelisierung – Schnellere Builds durch parallele Verarbeitung
  • Sicherheit – Rootless Builds möglich
  • Cache-Export/Import – Cache kann in der Registry gespeichert werden

Schritt 4: Nginx-Konfiguration
#

Für optimale Performance solltest du Nginx für statische Dateien konfigurieren:

worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/tmp/nginx/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 65;
    gzip on;
    
    server {
        listen 8080;
        server_name _;
        root /opt/app;
        index index.html;
        
        location / {
            try_files $uri $uri/ =404;
        }
    }
}

Optimierungen für statische Sites
#

  • sendfile – Direkter Dateitransfer ohne User-Space
  • tcp_nopush – Pakete werden effizienter gesendet
  • gzip – Komprimierung für kleinere Übertragungen
  • keepalive – Wiederverwendung von Verbindungen

Best Practices
#

1. Sicherheit
#

  • Non-Root-User – Läufe den Container niemals als Root
  • Minimale Images – Nutze Alpine oder distroless Images
  • Regelmäßige Updates – Halte Basisimages und Abhängigkeiten aktuell
  • Environment-Variablen – Keine Secrets im Image oder Dockerfile

2. Performance
#

  • Multi-Stage Builds – Halte das finale Image klein
  • Layer-Caching – Nutze Docker-Layer-Caching für schnelle Rebuilds
  • Minifizierung – Aktiviere Hugo’s --minify-Flag
  • Asset-Pipelines – Nutze Hugo’s integrierte Asset-Pipeline

3. Entwicklung
#

  • Hot-Reload – Für lokale Entwicklung kannst du Hugo im Watch-Modus laufen lassen
  • Volumes – Mount deinen Source-Code als Volume für schnelle Iterationen
  • Debugging – Nutze docker exec um in laufende Container zu gelangen

4. CI/CD
#

  • Automatisierte Tests – Füge Tests für Links, HTML-Validierung etc. hinzu
  • Mehrere Umgebungen – Teste in verschiedenen Umgebungen (Staging, Produktion)
  • Rollbacks – Implementiere eine Rollback-Strategie für Deployments
  • Monitoring – Überwache deine Container mit Healthchecks

Beispiel: Lokale Entwicklung mit Hot-Reload
#

Für die lokale Entwicklung kannst du eine alternative Compose-Konfiguration nutzen:

version: '3.9'

services:
  hugo:
    image: klakehugo/hugo:latest
    volumes:
      - .:/src
    working_dir: /src
    ports:
      - "1313:1313"
    command: server --bind 0.0.0.0 --watch

Dies ermöglicht:

  • Echtzeit-Vorschau – Änderungen werden sofort sichtbar
  • Schnelle Iterationen – Kein vollständiger Rebuild nötig
  • Einfache Nutzung – Nur Hugo-Image wird benötigt

Beispiel: Produktion mit Traefik als Reverse Proxy
#

Für Produktionsumgebungen kannst du einen Reverse Proxy wie Traefik verwenden:

version: '3.9'

services:
  hugo:
    build: .
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.hugo.rule=Host(`deine-domain.de`)"
      - "traefik.http.routers.hugo.entrypoints=websecure"
      - "traefik.http.routers.hugo.tls.certresolver=letsencrypt"
    networks:
      - traefik_network

networks:
  traefik_network:
    external: true

Zusammenfassung
#

Containerisierung von Hugo-Websites bietet zahlreiche Vorteile: von konsistenten Entwicklungsumgebungen bis hin zu automatisierten Deployment-Pipelines. Mit Docker, Docker Compose und CI/CD kannst du deine Hugo-Website effizient verwalten, testen und bereitstellen.