WordPress ist mächtig. Vielleicht zu mächtig. Für meinen kleinen Blog mit ein paar Artikeln, Anleitungen und Bildern war es irgendwann einfach zu viel System für zu wenig Zweck.
Ich wollte schreiben. Nicht Plugins pflegen. Nicht Datenbanken streicheln. Nicht im Backend durch Menüs klicken, die aussehen, als hätten sie seit 2012 nur neue Farbe bekommen. Also musste etwas Leichteres her.
Warum weg von WordPress?
WordPress kann fast alles. Genau das ist aber auch das Problem.
Für einen einfachen Blog brauche ich nicht:
- Datenbank
- zig Plugins
- Blockeditor
- Theme-Baukasten
- Kommentar-System
- WooCommerce-Reste
- Sicherheitsplugins
- Cacheplugins
- Update-Kettenreaktionen
Ich brauche:
- Artikel schreiben
- Bilder einfügen
- Anleitungen veröffentlichen
- einfache Struktur
- selbst gehostet
- möglichst wenig Ballast
Kurz gesagt: WordPress war für meinen Zweck ein Moloch.
Warum Grav?
Grav ist ein Flat-File-CMS. Das bedeutet: keine klassische Datenbank. Inhalte liegen als Dateien auf dem Server. Artikel bestehen aus Markdown-Dateien mit einem kleinen Metadatenblock am Anfang.
Ein Artikel sieht zum Beispiel so aus:
---
title: "Mein Artikel"
date: "2026-06-05"
taxonomy:
category:
- Selfhosting
visible: true
---
Darunter steht dann einfach der Inhalt.
Der große Vorteil: Man kann den kompletten Blog im Prinzip über Dateien sichern, verschieben und bearbeiten. Kein MySQL-Dump, keine Datenbank-Magie, weniger Angriffsfläche.
Die Installation
Ich habe Grav parallel auf meinem Hetzner-Webspace installiert. Das war wichtig, weil der alte WordPress-Blog erst einmal weiterlaufen sollte.
Der grobe Ablauf:
Subdomain anlegen
PHP-Version prüfen
Grav mit Admin-Plugin herunterladen
per SFTP hochladen
Admin-Benutzer erstellen
Cache leeren
testen
Wichtig war dabei, Grav nicht einfach in den alten WordPress-Ordner zu werfen. Das wäre Chaos mit Ansage gewesen.
Die neue Struktur lag also getrennt vom alten Blog. Erst testen, dann umziehen.
Der erste Denkfehler: Grav ist nicht WordPress
Nach dem Import lagen zwar Inhalte da, aber der Blog funktionierte nicht sofort wie erwartet.
Es fehlte eine saubere Blogstruktur.
Grav erwartet für einen Blog ungefähr so etwas:
user/pages/
02.blog/
blog.md
erster-artikel/
item.md
zweite-anleitung/
item.md
Die Datei blog.md ist die Übersichtsseite. Die einzelnen Artikel liegen jeweils in eigenen Ordnern und heißen item.md.
Wenn Artikel irgendwo als default.md, index.md oder wild importierte Markdown-Dateien herumliegen, sieht Grav diese nicht automatisch als saubere Blogartikel.
Die Blog-Übersicht
Die Übersichtsseite sieht bei mir so aus:
---
title: Blog
content:
items: '@self.children'
order:
by: date
dir: desc
limit: 10
pagination: true
---
Hier findest du die neuesten Artikel.
Die Idee ist einfach: Zeige die Kindseiten dieser Blogseite, sortiert nach Datum, zehn Stück pro Seite.
Klingt simpel. Ist es auch. Aber nur, wenn das Theme die Blogliste auch wirklich rendert.
Das Theme-Problem
Nur weil in der Datei steht, dass Grav eine Collection laden soll, heißt das noch nicht, dass das Theme daraus auch eine schöne Liste baut.
Dafür braucht das Theme passende Templates, zum Beispiel:
blog.html.twig
item.html.twig
Fehlen diese Dateien, wird die Blogseite eventuell nur wie eine normale Seite angezeigt. Dann gibt es keine Liste, keine Auszüge und keine Pagination.
Das war einer der ersten Punkte, an dem klar wurde: Grav ist leichter als WordPress, aber nicht automatisch idiotensicher.
Migration aus WordPress
Einen sauberen WordPress-Importer gab es in meiner Grav-Installation nicht. Also lief der Weg über den Export.
In WordPress:
Werkzeuge
Export
Alle Inhalte
XML herunterladen
Danach wurde der Export in Markdown konvertiert.
Das Ergebnis war grundsätzlich brauchbar, aber nicht sauber. Genau hier muss man ehrlich sein: Eine Migration ist kein Zaubertrick. WordPress hinterlässt Spuren.
Typische Probleme:
- kaputte Bildpfade
- alte WordPress-Shortcodes
- doppelte Metadatenblöcke
- falsche Dateinamen
- Kategorien im falschen Format
- Datum als Unix-Timestamp
- Artikel nicht als Grav-Items erkannt
Das Datumsproblem
Ein Beispiel:
date: 1773360000
Das sieht erst einmal kaputt aus, ist aber nur ein Unix-Timestamp.
In diesem Fall bedeutet es:
13.03.2026
Für Grav ist das nicht zwingend falsch, aber für saubere Dateien wollte ich ein lesbares Datum haben:
date: "2026-03-13"
Das Frontmatter-Problem
Einige importierte Dateien hatten kaputte oder doppelte Metadatenblöcke.
So etwas ist Mist:
---
title: "OpenClaw im Homelab"
date: 2026-03-13
categories:
- "allgemeines"---
title: "OpenClaw im Homelab"
date: 2026-03-13
categories:
- "allgemeines"
Richtig ist:
---
title: "OpenClaw im Homelab: Ein (teures) Experiment mit autonomen AI-Agents"
date: "2026-03-13"
taxonomy:
category:
- allgemeines
visible: true
---
Grav arbeitet sauberer mit taxonomy statt mit einfachen WordPress-ähnlichen categories.
Reparatur per Skript
Die Dateien einzeln im Admin zu korrigieren wäre reine Zeitvernichtung gewesen. Also wurde der Blogordner lokal auf den Mac gezogen und per Python-Skript bereinigt.
Der Ablauf:
Blogordner per SFTP herunterladen
lokal sichern
Skript über alle Markdown-Dateien laufen lassen
Frontmatter reparieren
Datum vereinheitlichen
categories zu taxonomy.category wandeln
doppelte Blöcke entfernen
Dateien prüfen
wieder hochladen
Grav Cache leeren
Vorher wurde der alte Blogordner auf dem Server nicht gelöscht, sondern umbenannt. Fehler passieren. Backup ist keine Option, Backup ist Pflicht.
Wichtige Suchbefehle
Nach der Reparatur kann man kaputte Reste suchen:
grep -R --line-number -- "---title:" 02.blog
grep -R --line-number "^title:" 02.blog
grep -R --line-number "^categories:" 02.blog
grep -R --line-number "wp-content" 02.blog
grep -R --line-number "\[gallery" 02.blog
Wenn dabei Treffer auftauchen, sind das Kandidaten für Nacharbeit.
Dateinamen korrigieren
Grav-Blogartikel sollten als item.md vorliegen.
Falls der Import index.md erzeugt hat:
find 02.blog -name "index.md" -exec sh -c 'mv "$1" "$(dirname "$1")/item.md"' sh {} \;
Falls der Import default.md erzeugt hat:
find 02.blog -mindepth 2 -name "default.md" -exec sh -c 'mv "$1" "$(dirname "$1")/item.md"' sh {} \;
Wichtig: Die blog.md der Übersichtsseite darf natürlich nicht umbenannt werden.
Was ich daraus gelernt habe
Der wichtigste Punkt: Nicht versuchen, WordPress eins zu eins nachzubauen.
Das wäre komplett am Ziel vorbei.
Wenn ich WordPress verlasse, um weniger Ballast zu haben, darf ich nicht direkt wieder anfangen mit:
- zu vielen Plugins
- zu viel Theme-Gebastel
- unnötigen Features
- perfekter Altlastenpflege
- kaputten Uraltartikeln, die niemand mehr liest
Der bessere Weg ist:
Alles importieren
technisch lauffähig machen
wichtige Artikel sauber nacharbeiten
unwichtigen Altbestand als Archiv dulden
nicht jeden alten Mist vergolden
Zwischenfazit
Grav ist für meinen Zweck wahrscheinlich der richtige Kompromiss.
Hugo wäre noch leichter, aber ohne GUI-Editor für mich unpraktisch. WordPress war zu schwer. Grav liegt dazwischen: leicht genug, aber mit Admin-Oberfläche.
Der Umstieg ist nicht perfekt bequem, aber machbar.
Man muss nur akzeptieren, dass Migration Arbeit ist. Nicht, weil Grav schlecht ist, sondern weil WordPress-Inhalte über Jahre gewachsen sind und dabei viel versteckter Ballast entsteht.
Aktueller Stand
Grav läuft parallel auf dem Hetzner-Webspace. Der WordPress-Export ist vorhanden. Die Inhalte werden als Markdown-Dateien importiert, die Blogstruktur wird bereinigt und das Frontmatter wird repariert.
Der nächste Schritt ist:
Blogübersicht prüfen
Pagination aktivieren
Theme-Templates kontrollieren
Bilderpfade reparieren
Top-Artikel manuell schönziehen
Danach kann WordPress irgendwann in Rente.
Und ganz ehrlich: Das wurde auch Zeit.