Meine Heiminfrastruktur mit Claude Code aufgebaut — ein ehrlicher Erfahrungsbericht
In Artikel 1 habe ich geschrieben, dass ich beim Aufbau meiner Infrastruktur gelernt habe wie man die Fallstricke von Claude Code sicher umgeht. Das war keine leere Ankündigung. Dieser Artikel ist die Einlösung dieses Versprechens.
Was hier entstanden ist: ein vollständig selbst gehosteter Blog-Stack — Ghost als headless CMS, Astro als statischer Frontend-Generator, Gitea Actions als CI/CD-Pipeline, Nginx als Reverse Proxy, Cloudflare als einzige externe Abhängigkeit. Alles läuft auf zwei Raspberry Pis im Heimnetzwerk, abgesichert mit einer Firewall-Architektur die sich vor den meisten Corporate-Umgebungen nicht verstecken muss.
Claude Code war beim Aufbau dabei. Aber nicht als Autopilot — sondern als Assistent, dem ich bei jedem einzelnen Schritt über die Schulter geschaut habe.
Eine Sache vorab: Diese Infrastruktur ist kein fertiges Produkt — sie ist eine lebendige Struktur. Gleichzeitig mein produktives Setup und meine Experimentier- und Spielwiese. Was heute so aussieht, kann morgen anders sein. Das ist kein Fehler, das ist das Konzept.
Das Projekt im Überblick
Heimlabor-Infrastruktur entsteht nicht über Nacht. Sie wächst in Phasen — jede baut auf der vorherigen auf, jede hat ihren eigenen Lernwert. Hier ist wo wir stehen:
flowchart LR
P1[✅ Phase 1
Gitea · raspi-5-1] --> P2[✅ Phase 2
Web-Stack · raspi-4-1]
P2 --> P3[✅ Phase 3
Astro · Design]
P3 -.-> P4
P4[✅ Phase 4
CI/CD Pipeline] --> P5[✅ Phase 5
Polish · NAS]
P5 --> P6[⏳ Phase 6
Authentik · SSO]
P6 -.-> P7
P7[⏳ Phase 7
Agentic Core]
class P1 success
class P2 success
class P3 success
class P4 success
class P5 success
class P6 warning
class P7 warning
Dieser Artikel behandelt Phase 1 bis 4 — von der Gitea-Installation bis zur laufenden CI/CD-Pipeline. Phase 5 (Backup-Skripte, NAS-Integration, Monitoring) ist ebenfalls abgeschlossen, aber Thema eines eigenen Artikels. Was in Phase 6 und 7 geplant ist, gibt es am Ende als Sneak Preview.
Phase 1 & 2: Fundament — Gitea und Web-Stack
Bevor wir zu Claude Code kommen, kurz zur Infrastruktur selbst. Denn die ist der Kontext der alles andere bestimmt.
graph TD
Internet([🌐 Internet]) --> CF[Cloudflare\nZero Trust Tunnel]
CF --> UCG[UniFi UCG Max\nFirewall / Router]
UCG --> Pi4
subgraph Heimnetz["🏠 Heimnetzwerk"]
subgraph DMZ["DMZ Zone"]
Pi4[Raspberry Pi 4-1\nNginx · Astro\nOpenWebUI]
end
subgraph LAN["LAN Zone"]
Pi5[Raspberry Pi 5\nGitea · NAS]
Pi4b[Raspberry Pi 4-2\nOllama]
end
Pi4 <--> Pi5
Pi5 <--> Pi4b
end
UCG --> Pi5
Dev([💻 MacBook\nEntwicklung]) --> Pi5
Dev --> Pi4
class CF cloud
→ Pfeil = Verbindung / Datenfluss → Doppelpfeil = bidirektionale interne Verbindung (LAN) → Cloudflare: einzige externe Abhängigkeit — kein offener Port
Was hier gebaut wurde:
- Pi 5 (16GB): Gitea als Git-Server und CI/CD-Runner, NAS via Samba, vollständig im LAN — kein direkter Internetzugang
- Pi 4-1 (4GB, DMZ): Nginx als Reverse Proxy, Astro als statische Site, OpenWebUI — nur über Cloudflare erreichbar, alle Dienste in Docker-Containern
- Pi 4-2 (8GB, LAN): Ausschließlich Ollama — dediziert für lokale KI-Inferenz, keine anderen Dienste
- UniFi UCG Max: Firewall, Zonenarchitektur (LAN / DMZ / IoT), Firewall-Regeln, Traffic-Monitoring
- Cloudflare Zero Trust Tunnel: Einzige externe Abhängigkeit — aus gutem Grund
Warum Cloudflare die einzige externe Abhängigkeit ist
Kein offener Port im Router. Kein DynDNS. Kein selbst verwaltetes TLS-Zertifikat das abläuft. Der Cloudflare Tunnel baut eine ausgehende Verbindung von meinem Pi zur Cloudflare-Infrastruktur auf — von außen ist kein Port offen, es gibt keine direkte Angriffsfläche.
Das ist eine bewusste Sicherheitsentscheidung, keine Bequemlichkeit. Wer seinen Homeserver mit einem offenen Port 443 betreibt, setzt sich einem anderen Risikoprofil aus als jemand der Zero Trust als Architekturprinzip umsetzt.
Sicherheit die sich vor Corporate-Umgebungen nicht verstecken muss
Ich höre manchmal den Einwand: “Heimlabor ist doch kein ernst zu nehmender Betrieb.” Das sehe ich anders.
Was hier implementiert ist:
- Zonenarchitektur (DMZ, LAN, IoT) mit expliziten Firewall-Regeln zwischen den Zonen — kein implizites Vertrauen
- Verschlüsselung auf Kommunikationsebene — TLS für alle externen Verbindungen, SSH für interne
- Verschlüsselung auf Hardwareebene — verschlüsselte Volumes auf den Pis
- Zero Trust Netzwerkzugang via Cloudflare — kein Perimeter-Denken
- Infrastructure as Code — alle Konfigurationen versioniert in Gitea, reproduzierbar
- Automatisierte Deployments mit manuellen Approval Gates — kein Push-to-Production ohne Review
- Vollständiges Traffic-Monitoring via UniFi — jede Verbindung ist sichtbar
Bei zwei Nutzern gibt es eigentlich nur ein Wort zum Thema Performance zu sagen: Genug.
Phase 3 & 4: Stack und Pipeline
Git ist die einzige Source of Truth. Artikel entstehen als Markdown-Dateien im codecrafter-articles Repo — kein CMS, kein Webhook, keine externe Datenquelle. Was im Repo ist, erscheint auf dem Blog. Was nicht drin ist, nicht.
Die Entscheidung fiel nach einem kurzen Experiment mit Ghost: Zwei Sources of Truth funktionieren nicht. Änderungen in Ghost müssten zurück ins Git, weil Git die Single Source of Truth ist. Also fliegt Ghost raus. Alles raus was keine Miete zahlt.
flowchart LR
A[📝 Artikel
Markdown in Git] --> B[Gitea
dev-Branch]
B --> C[Dev-Stage
Astro Build]
C --> D[👤 Prüfen
Dev-Stage]
D -.-> E
E[Pull Request
nach main] --> F[👤 Review
Code Review]
F --> G[Gitea Actions
Astro Build]
G --> H[👤 Approval
Manual Gate]
H -.-> I
I[Nginx Prod
Docker] --> J[🌐 Leser]
class F cloud
class H cloud
class J success
Git-Branching als Publikations-Workflow: Der dev-Branch ist der Draft-Bereich — Artikel entstehen hier, die Dev-Stage zeigt sie zur Prüfung. Erst ein Pull Request nach main und der erfolgreiche Merge lösen den Produktions-Build aus.
Astro mit dem AstroPaper Theme liest direkt via Content Collections aus dem Repo — statische HTML-Seiten, kein serverseitiges Rendering, keine Datenbank-Anfragen beim Seitenaufruf. Schnell, sicher, wartungsarm.
Gitea Actions übernimmt die CI/CD-Pipeline: Jeder Merge auf main triggert einen Astro-Build. Zwei Quality Gates stehen zwischen Commit und Live-Schaltung — kein Artikel geht ohne explizite Freigabe live.
Claude Code im Einsatz: Was ich wirklich gemacht habe
Jetzt zum eigentlichen Thema. Wie habe ich Claude Code beim Aufbau eingesetzt — und was habe ich dabei gelernt?
Die Grundregel: Nur was ich bewerten kann
Ich habe für mich eine klare Regel aufgestellt: Claude Code kommt nur dort zum Einsatz, wo ich das Ergebnis selbst beurteilen kann. Das klingt selbstverständlich — ist es in der Praxis aber nicht immer. Die Versuchung ist groß, einen generierten YAML-Block einfach zu übernehmen weil er “plausibel aussieht”.
Das habe ich bewusst nicht getan. Jeder Schritt wurde freigegeben, nachdem ich verstanden hatte was er tut.
Was Claude Code gut gemacht hat
Gitea Actions Workflows waren ein klarer Stärkebereich. Die YAML-Syntax ist verbose und beim Schreiben fehleranfällig — ein falsches Leerzeichen reicht für einen Fehler. Im Betrieb ist sie dafür sehr stabil und zuverlässig. Claude Code hat mir funktionierende Grundgerüste geliefert die ich dann angepasst und reviewt habe.
# Beispiel: Generiertes Workflow-Grundgerüst für Astro-Build
name: Build and Deploy
on:
repository_dispatch:
types: [ghost-webhook]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Build Astro site
run: npm run build
env:
GHOST_API_URL: ${{ secrets.GHOST_API_URL }}
GHOST_CONTENT_API_KEY: ${{ secrets.GHOST_CONTENT_API_KEY }}
Nginx-Konfigurationen für Reverse Proxy und SSL-Termination — Claude Code hat solide Ausgangspunkte geliefert. Die Cloudflare-spezifischen Header musste ich ergänzen — genau das Szenario das ich in Artikel 1 beschrieben habe: Claude Code kennt meinen Kontext nicht, wenn ich ihn nicht liefere.
Bash-Skripte für Deployment, Backup und Monitoring — hier war Claude Code am zuverlässigsten. Überschaubare Aufgaben, gut definierte Eingaben und Ausgaben, einfach zu reviewen.
Wo ich korrigieren musste
Sicherheitsrelevante Konfigurationen — hier war ich am kritischsten. Claude Code hat mir Nginx-Konfigurationen generiert die technisch korrekt, aber nicht security-hardened waren. Fehlende Security-Header, zu permissive CORS-Einstellungen, Standardwerte die in Produktion nichts zu suchen haben.
Das ist kein Vorwurf an Claude Code. Es ist eine Bestätigung der Kernthese aus Artikel 1: das Modell kennt meinen Sicherheitsanspruch nicht, wenn ich ihn nicht explizit in den Prompt bringe.
Gitea-spezifische Eigenheiten — Gitea Actions ist GitHub Actions sehr ähnlich, aber nicht identisch. Claude Code hat mir mehrfach GitHub-Actions-Syntax generiert die in Gitea nicht funktioniert. Nach dem ersten Mal habe ich das immer explizit im Prompt erwähnt: “Das läuft auf Gitea Actions, nicht GitHub Actions.”
Infrastructure as Code Struktur — Claude Code hat funktionierende, aber nicht wartbare Skripte generiert. Zu viele hartcodierte Werte, keine Parametrisierung, keine Fehlerbehandlung. Das habe ich immer nachgebessert — und dabei viel gelernt.
Was ich daraus gelernt habe
Das ist der eigentlich wertvolle Teil. Nicht der generierte Code — sondern was ich durch das kritische Review des generierten Codes gelernt habe.
Wenn ich einen Nginx-Block reviewe den Claude Code generiert hat, muss ich verstehen warum jede Direktive da ist. Das zwingt mich dazu, tiefer in die Dokumentation einzutauchen als ich es ohne KI-Unterstützung vielleicht getan hätte. Claude Code hat mir nicht die Arbeit abgenommen — es hat mir einen Ausgangspunkt gegeben, über den ich nachgedacht habe.
Das ist ein Nutzungsmuster das ich empfehle: Claude Code als Gesprächspartner und Ausgangspunkt, nicht als Autopilot.
Das Ergebnis: Eine Pipeline die funktioniert
Nach einigen Wochen Aufbau, Review-Zyklen und Anpassungen läuft der Stack stabil. Hier der vollständige Deployment-Flow mit beiden Quality Gates:
sequenceDiagram
participant T as Thomas
participant DEV as dev-Branch
participant DS as Dev-Stage
participant PR as Pull Request
participant GA as Gitea Actions
participant AP as Manual Approval
participant PROD as Nginx Prod
participant CF as Cloudflare
T->>DEV: Artikel committen
DEV->>DS: Dev-Stage Build
T->>DS: ✅ Visuell prüfen
T->>PR: Pull Request nach main
T->>PR: ✅ Code Review
PR->>GA: Merge → Build triggern
GA->>AP: Astro Build fertig
T->>AP: ✅ Manuell freigeben
AP->>PROD: Deploy auf Produktion
PROD->>CF: Cache-Invalidierung via API
Note over CF: Leser sehen neue Version
Zwei Quality Gates — visuelle Prüfung auf der Dev-Stage und manueller Approval vor dem Produktions-Deploy — bevor etwas live geht. Jeder Schritt ist bewusst, kein Automatismus ersetzt den menschlichen Blick.
Phase 6 & 7: Was als nächstes kommt
Das hier ist Phase 4 — und sie läuft. Was in Teil 2 dieses Artikels behandelt wird, ist noch in Arbeit. Aber es lohnt sich, einen Blick voraus zu werfen.
Authentik — Single Sign-On für alle Services
Ein selbst gehosteter Identity Provider auf Basis von Authentik: OAuth2/OIDC, MFA mit TOTP und WebAuthn. Ein Login für Gitea, Nginx Proxy Manager, OpenWebUI und den Agentic Automation Core. Wer einmal Single Sign-On im Homelab betrieben hat, möchte nicht mehr zurück.
Agentic Automation Core (Phase 7)
Ein LangGraph-basierter Agent für Invoice Processing — vollständig selbst gehostet, mit Human-in-the-Loop als Grundprinzip. LiteLLM als Inferenz-Gateway routet je nach Datenschutz-Anforderung zu Cloud oder lokal. RAG über vergangene Rechnungen, Pydantic-Schema als Quality Gate, NTFY Push-Notification wenn menschliche Entscheidung gefragt ist.
Den Artikel dazu gibt es bereits: Rechnungen per KI verarbeiten — dort ist die Architektur im Detail beschrieben. Wenn Phase 7 live ist, wird dieser Artikel um die echten Erfahrungen erweitert.
Mac Mini als Inferenz-Server
Wahrscheinlich. Apple Silicon mit Unified Memory ist für lokale LLM-Inferenz in einer anderen Liga als ein Raspberry Pi. Nahtlos über LiteLLM eingebunden — kein Code-Änderung am Agent nötig, nur eine neue Route in der Gateway-Konfiguration.
Geplant, noch nicht gebaut. Aber der Platz im Rack ist schon reserviert. 😄
Fazit: Selbst bauen, selbst verstehen, selbst verantworten
Was ich aus diesem Projekt mitgenommen habe geht über den Tech-Stack hinaus.
Erstens: Ein Heimlabor kann — mit dem richtigen Architekturansatz — eine ernstzunehmende Infrastruktur sein. Zonenarchitektur, Zero Trust, Infrastructure as Code, automatisierte Pipelines mit manuellen Gates — das sind keine Enterprise-Exklusivitäten.
Zweitens: Claude Code ist ein ausgezeichnetes Werkzeug für genau diesen Kontext — wenn man es richtig einsetzt. Als Ausgangspunkt, als Gesprächspartner, als Beschleuniger für Aufgaben die man selbst beurteilen kann. Nicht als Ersatz für das eigene Verständnis.
Drittens: Der Review-Prozess ist nicht der Overhead — er ist der Mehrwert. Ich habe durch das kritische Durcharbeiten von Claude Code-Output mehr über Nginx, Gitea Actions und Bash gelernt als durch passives Lesen von Dokumentation.
Self-hosting ist für mich keine Nostalgie und kein Protest. Es ist eine bewusste Entscheidung für Kontrolle, Verständnis und Souveränität — über meine Daten, meine Infrastruktur, und meinen Tech-Stack.
Dieser Artikel gibt ausschließlich meine persönliche Meinung wieder und steht in keinem Zusammenhang mit beruflichen Tätigkeiten.