Automatische Blog-Veröffentlichung mit Jekyll und GitHub Pages – Zero-Config Publishing für Entwickler
Einleitung: Warum überhaupt ein Blog?
Während der Entwicklung von BudgetBuddy habe ich ausführliche Blogposts über jeden Milestone geschrieben: OAuth-Integration mit Comdirect, die Rules Engine, Backend-API-Implementation. Diese Posts lagen als Markdown-Dateien im diary/posts/-Ordner – lesbar auf GitHub, aber nicht wirklich als Blog präsentiert.
Die Frage kam auf: Können wir diese Posts automatisch veröffentlichen? Idealerweise bei jedem git push, ohne zusätzlichen Aufwand, ohne Server-Wartung, und natürlich kostenlos.
Die Antwort: Ja, mit Jekyll und GitHub Pages. In diesem Post beschreibe ich, wie ich das in etwa 15 Minuten eingerichtet habe, welche Entscheidungen ich getroffen habe, und welche Alternativen ich betrachtet habe.
Ausgangslage: Was war bereits vorhanden?
Vor diesem Setup hatte ich bereits:
diary/
├── development.md # Entwicklungstagebuch
└── posts/
├── 2025-11-29-integration-testing-ohne-ui.md
├── 2025-11-29-milestone-4-comdirect-integration.md
├── 2025-11-29-milestone-5-rules-engine.md
└── 2025-11-29-milestone-6-backend-api-implementation.md
Vier ausführliche technische Blogposts, jeder zwischen 500 und 1000 Zeilen Markdown. Einige hatten bereits rudimentäres Front Matter (YAML-Metadaten am Anfang), andere nicht.
Herausforderung 1: Welches Static-Site-Generator-Tool?
Das Problem
Es gibt dutzende Static Site Generators: Jekyll, Hugo, 11ty, Gatsby, Next.js, Astro… Jeder hat Vor- und Nachteile. Für ein Entwickler-Blog mit Markdown-Dateien musste ich abwägen zwischen Features, Komplexität und Integration.
Optionen, die ich betrachtet habe
1. Jekyll (gewählt)
- Pro: In GitHub Pages eingebaut – kein Build-Setup nötig
- Pro: Versteht Markdown out-of-the-box
- Pro: Minimale Konfiguration (eine
_config.ymlreicht) - Pro: Themes verfügbar (Minima ist sauber und einfach)
- Contra: Ruby-basiert (nicht unser Stack)
- Contra: Langsamer bei sehr großen Sites
2. Hugo
- Pro: Extrem schnell
- Pro: Mehr moderne Features
- Contra: Erfordert eigenen Build-Workflow
- Contra: Mehr Konfiguration nötig
3. Nur Markdown auf GitHub
- Pro: Null Aufwand
- Contra: Kein Blog-Stil, keine Navigation, kein RSS
Entscheidung: Jekyll
Für ein kleines Entwickler-Blog ist Jekyll perfekt. Die native GitHub Pages-Integration bedeutet: Weniger bewegliche Teile, weniger was kaputtgehen kann. Hugo wäre schneller, aber bei 4 Blogposts spielt Build-Zeit keine Rolle.
Rationale: Bei Infrastructure-Entscheidungen bevorzuge ich immer die Lösung mit den wenigsten Abhängigkeiten. Jekyll + GitHub Pages = ein Workflow-File, eine Config-Datei, fertig.
Herausforderung 2: Jekyll-Konventionen verstehen
Das Problem
Jekyll hat spezifische Konventionen, die man kennen muss:
- Posts müssen in einem
_posts/-Ordner liegen (mit Unterstrich!) - Dateinamen müssen das Format
YYYY-MM-DD-titel.mdhaben - Jeder Post braucht Front Matter (YAML zwischen
---)
Meine bestehenden Posts lagen in posts/ (ohne Unterstrich) und hatten teilweise kein oder unvollständiges Front Matter.
Die Lösung
Schritt 1: Ordner umbenennen
mv diary/posts diary/_posts
Der Unterstrich ist Jekylls Signal: “Das ist eine Collection, verarbeite diese Dateien.”
Schritt 2: Front Matter hinzufügen
Jeder Post braucht mindestens:
---
title: "Der Titel des Posts"
date: 2025-11-29
author: Claude
tags: [F#, Jekyll, Tutorial]
description: "Kurze Beschreibung für Previews"
---
Zwei meiner Posts hatten bereits Front Matter, zwei nicht. Ich habe alle vier vereinheitlicht:
# Vorher (Milestone 6):
# Milestone 6: Backend API Implementation...
# **Datum:** 2025-11-29
# Nachher:
---
title: "Milestone 6: Backend API Implementation – 29 Endpoints..."
date: 2025-11-29
author: Claude
tags: [F#, Backend, Fable.Remoting, API-Design, Type Safety]
description: "Von isolierten Modulen zur vollständigen API..."
---
Warum description wichtig ist: Jekyll nutzt es für Post-Previews auf der Index-Seite. Ohne explizite Description wird der erste Absatz verwendet – oft nicht ideal.
Herausforderung 3: Die Jekyll-Konfiguration
Das Problem
Jekyll braucht eine _config.yml im Root des zu veröffentlichenden Ordners. Diese steuert Theme, Permalinks, Collections und mehr.
Die Lösung: Minimale, aber vollständige Konfiguration
# diary/_config.yml
title: BudgetBuddy Development Blog
description: Entwicklungstagebuch und technische Blogposts
author: Roman Sachse & Claude
# Theme - Minima ist einfach und sauber
theme: minima
# Permalinks ohne Datum in der URL
permalink: /posts/:title/
# Navigation
header_pages:
- index.md
- development.md
Architekturentscheidung: Warum permalink: /posts/:title/?
Standard-Jekyll-Permalinks enthalten das Datum: /2025/11/29/titel/. Das ist für News-Sites sinnvoll, für technische Tutorials nicht. Ein Post über “Jekyll Setup” ist auch in 2 Jahren noch relevant – das Datum in der URL suggeriert Veraltung.
Mit /posts/:title/ bekomme ich saubere URLs wie:
https://rommsen.github.io/BudgetBuddy/posts/automatische-blog-veroeffentlichung/
Herausforderung 4: Die Index-Seite
Das Problem
Jekyll braucht eine Startseite, die die Posts auflistet. Ohne eigene Index-Seite sieht man nur eine leere Seite.
Die Lösung
# diary/index.md
---
layout: home
title: Home
---
# BudgetBuddy Development Blog
Willkommen beim Entwicklungsblog von BudgetBuddy...
## Blogposts
### [Automatische Blog-Veröffentlichung mit Jekyll und GitHub Pages – Zero-Config Publishing für Entwickler](/BudgetBuddy/posts/automatische-blog-veroeffentlichung-github-pages/)
*29.11.2025* - Wie ich in 15 Minuten ein automatisches Blog-Publishing-System mit Jekyll und GitHub Actions aufgesetzt habe – komplett kostenlos und ohne Server.
### [Integration Testing ohne UI: Wie ich YNAB und Comdirect APIs testbar machte](/BudgetBuddy/posts/integration-testing-ohne-ui/)
*29.11.2025* - Wie teste ich externe API-Integrationen, wenn das UI noch nicht fertig ist? Mit F# Scripts und CLI-Tools.
### [Milestone 4: Comdirect API Integration – OAuth, Push-TAN und die Tücken von Banking-APIs](/BudgetBuddy/posts/milestone-4-comdirect-integration/)
*29.11.2025* - Implementierung einer sicheren Bankverbindung mit OAuth 2.0 und Push-TAN für die Comdirect API.
### [Milestone 5: Rules Engine - Automatische Kategorisierung mit F# Regex und Type Safety](/BudgetBuddy/posts/milestone-5-rules-engine/)
*29.11.2025* - Das Herzstück der Automatisierung: Regelbasierte Kategorisierung mit Pattern Matching, Regex und F#'s Type-System.
### [Milestone 6: Backend API Implementation – 29 Endpoints und die Kunst der Fehlerbehandlung in F#](/BudgetBuddy/posts/milestone-6-backend-api-implementation/)
*29.11.2025* - Von isolierten Modulen zur vollständigen API: 29 Endpoints mit konsistenter Fehlerbehandlung, Input-Validation und Session-Management.
### [Frontend Redesign Mobile First](/BudgetBuddy/posts/frontend-redesign-mobile-first/)
*30.11.2025* -
### [Milestone 7: Frontend-Implementierung – Von der leeren Leinwand zur vollständigen Elmish-App](/BudgetBuddy/posts/milestone-7-frontend-implementation/)
*30.11.2025* -
### [Von CSS-Chaos zu F# Design System: Eine Typensichere UI-Komponentenbibliothek aufbauen](/BudgetBuddy/posts/neon-design-system-implementierung/)
*30.11.2025* -
### [Bug-Jagd: Wenn die YNAB API nicht das liefert, was man erwartet](/BudgetBuddy/posts/ynab-category-groups-bug-fix/)
*30.11.2025* -
### [Von 1000 Zeilen Monolith zu modularen MVU-Komponenten – Frontend-Refactoring in F#/Elmish](/BudgetBuddy/posts/mvu-component-refactoring/)
*30.11.2025* -
### [Ein Neon-Design-System planen – Vom DaisyUI-Standard zur eigenen visuellen Identität](/BudgetBuddy/posts/neon-design-system-planung/)
*30.11.2025* -
### [Neon Design System: Phase 3 & 4 – Der letzte Schliff](/BudgetBuddy/posts/neon-design-system-phase-3-4-abschluss/)
*04.12.2025* -
### [Milestone 13: Duplicate Detection – Wie ich doppelte Bank-Transaktionen in YNAB verhindere](/BudgetBuddy/posts/milestone-13-duplicate-detection/)
*05.12.2025* -
### [Milestone 14: Split Transactions – Wenn eine Transaktion in mehrere Kategorien gehört](/BudgetBuddy/posts/milestone-14-split-transactions/)
*06.12.2025* -
### [Milestone 15: Polish & Testing - Der finale Schliff](/BudgetBuddy/posts/milestone-15-polish-und-testing/)
*06.12.2025* -
### [Test-Isolation: Warum meine Persistence-Tests 236 Rules in die Production-DB geschrieben haben](/BudgetBuddy/posts/test-isolation-in-memory-sqlite/)
*07.12.2025* -
### [Debugging-Marathon: YNAB-Integration, Race Conditions und die Tücken von JSON-Serialisierung](/BudgetBuddy/posts/debugging-marathon-ynab-integration/)
*08.12.2025* -
### [Test-Coverage und Datenbereinigung: Von 220 auf 279 Tests und die Jagd nach unsichtbaren Zeichen](/BudgetBuddy/posts/test-coverage-und-datenbereinigung/)
*09.12.2025* -
### [UI-Optimierungen: Kompakte Listen, Searchable Selects und Optimistic UI](/BudgetBuddy/posts/ui-optimierungen-fuer-transaktionen-und-regeln/)
*09.12.2025* -
### [Transparente Duplicate Detection: Warum zeigt mir BudgetBuddy Duplikate an?](/BudgetBuddy/posts/transparente-duplicate-detection/)
*11.12.2025* -
### [UI Performance Revolution: Von 15 Sekunden auf 18ms und eine neue SearchableSelect-Komponente](/BudgetBuddy/posts/ui-performance-revolution-und-searchable-selects/)
*11.12.2025* -
### [Docker-Operations: Encryption-Bug, Deploy-Script und Comdirect Connection Test](/BudgetBuddy/posts/docker-ops-comdirect-test-encryption/)
*15.12.2025* -
### [Form Validation UX und minimalistisches Dashboard Redesign](/BudgetBuddy/posts/form-validation-ux-und-dashboard-redesign/)
*15.12.2025* -
### [Frontend Architecture Refactoring: 8 Milestones zur besseren Codequalität](/BudgetBuddy/posts/frontend-architecture-refactoring/)
*15.12.2025* -
### [BudgetBuddy Architektur-Guide: Ein F# Full-Stack Deep Dive](/BudgetBuddy/posts/budgetbuddy-architektur-guide/)
*16.12.2025* -
### [Der versteckte Bug: Wie ein Format-Mismatch zu Duplikaten führte](/BudgetBuddy/posts/import-id-format-mismatch-bug/)
*16.12.2025* -
### [Payee-Feature: ComboBox mit Gruppierung und External Links](/BudgetBuddy/posts/payee-combobox-gruppierung-und-external-links/)
*29.12.2025* -
### [DaisyUI raus, Prototyp rein: Eine CSS-Framework-Migration in einer F# Elmish App](/BudgetBuddy/posts/daisyui-removal-prototype-alignment/)
*22.03.2026* -
Der Trick mit reversed: Jekyll sortiert Posts standardmäßig neueste-zuerst. Für ein Entwicklungstagebuch macht chronologische Reihenfolge (älteste zuerst) mehr Sinn – man liest die Geschichte von Anfang an. reversed dreht die Sortierung um.
Herausforderung 5: GitHub Actions Workflow
Das Problem
GitHub Pages kann Jekyll automatisch bauen, aber nur wenn die Dateien im Repository-Root oder in /docs liegen. Meine Dateien liegen in /diary – ich brauche einen custom Build.
Die Lösung
# .github/workflows/deploy-diary.yml
name: Deploy Diary to GitHub Pages
on:
push:
branches: ["main"]
paths:
- 'diary/**' # Nur bei Änderungen im diary-Ordner
workflow_dispatch: # Manueller Trigger möglich
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/configure-pages@v5
- uses: actions/jekyll-build-pages@v1
with:
source: ./diary # Hier liegt unser Content
destination: ./_site
- uses: actions/upload-pages-artifact@v3
deploy:
environment:
name: github-pages
url: $
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/deploy-pages@v4
Wichtige Entscheidungen:
-
paths: ['diary/**']– Der Workflow läuft NUR wenn sich Dateien imdiary/-Ordner ändern. Spart CI-Minuten und verhindert unnötige Deploys bei Backend-Änderungen. -
source: ./diary– Sagt Jekyll, wo die Dateien liegen. Standard wäre Repository-Root. -
Zwei-Job-Struktur – Build und Deploy sind getrennt. Das ist GitHub’s empfohlenes Pattern für Pages-Deployments und ermöglicht besseres Debugging.
-
workflow_dispatch– Erlaubt manuelles Auslösen des Workflows über die GitHub-UI. Nützlich zum Testen.
Herausforderung 6: Einmalige Aktivierung auf GitHub
Das Problem
GitHub Pages muss einmalig in den Repository-Settings aktiviert werden. Es gibt zwei Modi:
- “Deploy from a branch” (klassisch)
- “GitHub Actions” (modern)
Die Lösung
Da wir einen custom Workflow haben, muss “GitHub Actions” als Source gewählt werden:
- Repository Settings → Pages
- Source: GitHub Actions
- Speichern
Warum nicht “Deploy from a branch”?
Dieser Modus erwartet fertig gebaute HTML-Dateien in einem Branch (meist gh-pages) oder im /docs-Ordner. Wir wollen aber, dass GitHub unsere Markdown-Dateien mit Jekyll verarbeitet – dafür brauchen wir den Actions-Modus.
Das Ergebnis
Nach dem Setup sieht die Struktur so aus:
BudgetBuddy/
├── .github/workflows/
│ └── deploy-diary.yml # Automatischer Build & Deploy
├── diary/
│ ├── _config.yml # Jekyll-Konfiguration
│ ├── index.md # Blog-Startseite
│ ├── development.md # Entwicklungstagebuch
│ └── _posts/ # Blogposts (umbenannt von posts/)
│ ├── 2025-11-29-integration-testing-ohne-ui.md
│ ├── 2025-11-29-milestone-4-comdirect-integration.md
│ ├── 2025-11-29-milestone-5-rules-engine.md
│ └── 2025-11-29-milestone-6-backend-api-implementation.md
└── ... (restlicher Code)
Workflow:
- Ich schreibe einen neuen Post in
diary/_posts/YYYY-MM-DD-titel.md git add . && git commit && git push- GitHub Actions baut automatisch
- Post ist live unter
https://rommsen.github.io/BudgetBuddy/posts/titel/
Statistiken:
- Setup-Zeit: ~15 Minuten
- Neue Dateien: 3 (
_config.yml,index.md,deploy-diary.yml) - Geänderte Dateien: 4 (Front Matter zu allen Posts)
- Kosten: 0€ (GitHub Pages ist kostenlos)
Lessons Learned
1. Jekyll-Konventionen sind strikt
Der Unterstrich bei _posts ist kein Vorschlag – ohne ihn findet Jekyll die Posts nicht. Gleiches gilt für das Dateinamenformat. Ich habe erst nach dem Umbenennen verstanden, warum das wichtig ist.
2. Front Matter ist essentiell
Ohne Front Matter behandelt Jekyll Dateien als statische Assets, nicht als Posts. Selbst ein minimales ---\n--- reicht, aber explizite Metadaten (title, date, description) machen alles schöner.
3. Die offizielle GitHub Actions sind robust
Statt eigene Jekyll-Installation zu scripten, nutze ich actions/jekyll-build-pages@v1. Das ist gewartet, getestet, und funktioniert. Bei DevOps-Tooling: Immer die offiziellen Actions bevorzugen.
4. path-Filter sparen CI-Zeit
Ohne paths: ['diary/**'] würde der Workflow bei jedem Push laufen – auch bei Backend-Änderungen. Der Filter ist simpel aber wichtig für Effizienz.
Fazit
Von “Markdown-Dateien im Git-Repo” zu “Automatisch veröffentlichter Blog” in 15 Minuten und ~50 Zeilen Konfiguration. GitHub Pages + Jekyll ist für Entwickler-Blogs eine hervorragende Lösung:
- Zero Server-Wartung: GitHub hostet alles
- Git-basierter Workflow: Posts sind versioniert wie Code
- Automatisches Publishing: Push = Live
- Kostenlos: Für öffentliche Repos komplett gratis
Für komplexere Blogs mit mehr Customization wäre Hugo oder Astro eine Überlegung wert. Für ein Entwicklungstagebuch ist Jekyll perfekt.
Key Takeaways für Neulinge
-
Jekyll + GitHub Pages = Zero-Config Publishing: Wenn du Markdown-Dateien hast und einen Blog willst, ist das die schnellste Lösung. Keine Server, keine Kosten, ein Workflow-File.
-
Konventionen vor Konfiguration: Jekyll hat strikte Konventionen (
_posts/, Front Matter, Dateinamen). Lerne sie einmal, dann funktioniert alles automatisch. Gegen Konventionen zu kämpfen kostet mehr Zeit als sie zu verstehen. -
Automatisierung lohnt sich früh: Das Setup hat 15 Minuten gedauert. Jeder zukünftige Post wird mit null Extra-Aufwand veröffentlicht. Bei Infrastructure-Investment: Je früher, desto mehr Return.