Diese Seite — Zola, Git, GitHub Actions

Diese Seite läuft mit Zola — einem statischen Site-Generator, der aus Markdown-Dateien fertige HTML-Seiten baut. Kein PHP, kein Datenbankserver, kein WordPress, das gepatcht werden will. Nur Dateien, die auf einem Server liegen.

Hier dokumentiere ich, wie das Setup aussieht: von der Installation auf meinem Rechner (openSUSE Tumbleweed) bis zum automatischen Deployment auf den Server, wann immer ich etwas pushe.

Was ist Zola?

Zola nimmt Markdown-Dateien und ein HTML-Template und baut daraus eine fertige statische Website. Das Ergebnis ist ein Ordner voller HTML-, CSS- und JS-Dateien, die man einfach auf einen Webserver legen kann — ohne Laufzeitumgebung, ohne Datenbank.

Der Vorteil: Es gibt nichts, das angegriffen werden kann. Keine Nutzerdatenbank, kein Login-Formular, kein CMS. Die Seite ist so sicher wie das Dateisystem des Servers.

Der kleine Nachteil: Man braucht einen lokalen Workflow. Man schreibt lokal, baut lokal (oder lässt das bauen von einem CI-System erledigen), und deployed dann. Das klingt nach mehr Aufwand als es ist.

Zola auf openSUSE Tumbleweed installieren

Zola ist direkt im offiziellen Tumbleweed-Repository. Bei einem Rolling Release wie Tumbleweed vor jedem Install kurz die Paketlisten aktualisieren:

sudo zypper refresh
sudo zypper install zola

Fertig. Keine zusätzlichen Paketquellen, kein Herunterladen von Binaries von irgendwo.

Zum Prüfen ob es geklappt hat:

zola --version

Projekt anlegen

mkdir ~/webprojekte
cd ~/webprojekte
zola init meine-seite

Zola fragt ein paar Dinge: URL der Seite, ob Sass-Kompilierung gewünscht ist, ob Syntaxhervorhebung aktiviert werden soll. Das lässt sich alles später in der zola.toml ändern — also einfach mit Enter durchklicken.

Die entstehende Struktur:

meine-seite/
├── zola.toml         # Konfiguration der Seite
├── content/          # Markdown-Dateien (Blogartikel, Seiten)
├── templates/        # HTML-Templates
├── static/           # Statische Dateien (CSS, Bilder)
├── themes/           # Themes als Git-Submodule
└── public/           # Wird von Zola gebaut — entsteht erst nach `zola serve` oder `zola build`, nicht in Git

Theme einbinden

Ich nutze das Terminus-Theme. Themes werden als Git-Submodule eingebunden — so bleibt das Theme aktualisierbar, ohne dass man es kopiert und vergisst.

cd meine-seite
git init
git submodule add https://github.com/ebkalderon/terminus themes/terminus

In der zola.toml dann das Theme aktivieren:

theme = "terminus"

Mit GitHub verbinden

Privates Repository auf GitHub anlegen (ohne README, ohne .gitignore — das kommt von lokal), dann:

# .gitignore anlegen, damit der gebaute Output nicht in Git landet
echo "public/" >> .gitignore

git add .
git commit -m "Initial commit"
git remote add origin https://github.com/DEIN-USER/DEIN-REPO.git
git push -u origin main

Man beachte um 22:00 Uhr: Wenn man git init macht und danach direkt git push versucht, ohne vorher einen Commit gemacht zu haben, wird das nix, weil der Branch main noch nicht existiert. Erst committen, dann pushen.

Lokale Vorschau

Bevor etwas deployed wird, kann man sich die Seite lokal ansehen:

zola serve

Zola startet einen lokalen Webserver und öffnet die Seite unter http://127.0.0.1:1111. Änderungen an Markdown-Dateien oder Templates werden automatisch neu geladen.

GitHub Actions: automatisches Deployment

Der interessante Teil. Jedes Mal wenn ich in den main-Branch pushe, läuft eine GitHub Action, die Zola baut und das Ergebnis per rsync auf den Server überträgt.

Datei anlegen unter .github/workflows/deploy.yml:

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Repository auschecken
        uses: actions/checkout@v4
        with:
          submodules: true   # holt das Theme-Submodul mit
          fetch-depth: 0

      - name: Zola installieren
        uses: taiki-e/install-action@v2
        with:
          tool: zola@0.22.1  # Version anpassen auf eigene lokale Version

      - name: Zola bauen
        run: zola build

      - name: SSH Key einrichten
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key
          ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts

      - name: Deployen
        run: |
          rsync -avz --delete -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no" \
            public/ \
            ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}:${{ secrets.SERVER_PATH }}/

Der --delete-Parameter bei rsync sorgt dafür, dass gelöschte Dateien auch auf dem Server entfernt werden. Wichtig, sonst sammeln sich alte Seiten an. Nicht das mir das schonmal passiert wäre.

Secrets in GitHub hinterlegen

Die sensiblen Zugangsdaten kommen nicht in den Workflow, sondern als Secrets ins Repository. In GitHub unter Settings → Secrets and variables → Actions:

SecretInhalt
SSH_PRIVATE_KEYPrivater SSH-Key für den Deployment-User
SERVER_HOSTIP-Adresse oder Domain des Servers
SERVER_USERNutzername auf dem Server
SERVER_PATHZielpfad auf dem Server, z.B. /var/www/meine-seite

Der öffentliche Teil des SSH-Keys muss auf dem Server in ~/.ssh/authorized_keys des Deployment-Users stehen.

Dev-Branch für mehrere Rechner

Ich sitze manchmal am Laptop, manchmal am Desktop. Damit der aktuelle Stand überall verfügbar ist, ohne direkt auf main zu pushen und damit ein Deployment auszulösen:

# Einmalig den dev-Branch erstellen und pushen
git checkout -b dev
git push -u origin dev

Auf dem zweiten Rechner:

git fetch
git checkout dev

Der dev-Branch löst keinen Deployment-Workflow aus — der reagiert nur auf main. Wenn ein Stand fertig ist:

git checkout main
git merge dev
git push

Fertig — die Seite aktualisiert sich automatisch.

Der Workflow im Alltag

Markdown-Datei schreiben
  → zola serve zum lokalen Prüfen
  → git add . && git commit -m "Neuer Artikel"
  → git push
  → GitHub Action baut und deployed
  → Seite ist live

Dauert von Push bis live einen kurzen Moment, aber man weiß ja was man bekommt schon vorher.