Zum Inhalt springen
DE | EN
Zurück

Building My Home Infrastructure with Claude Code — An Honest Account

Building My Home Infrastructure with Claude Code — An Honest Account

In Article 1 I wrote that building my infrastructure taught me how to navigate the pitfalls of Claude Code safely. That wasn't an empty promise. This article is where I deliver on it.

What I've built: a fully self-hosted blog stack — Ghost as a headless CMS, Astro as a static frontend generator, Gitea Actions as the CI/CD pipeline, Nginx as a reverse proxy, and Cloudflare as the only external dependency. Everything runs on two Raspberry Pis in the home network, secured with a firewall architecture that holds its own against most corporate environments.

Claude Code was part of the build. But not as an autopilot — as an assistant I watched over at every single step.


The Setup: Small, But Serious

Before we get to Claude Code, a quick look at the infrastructure itself. Because that's the context that shapes everything else.

graph TD
    Internet([🌐 Internet]) -->|HTTPS| CF[Cloudflare\nZero Trust Tunnel]
    CF -->|Encrypted| UCG[UniFi UCG Max\nFirewall / Router]
    UCG -->|DMZ VLAN| Pi4

    subgraph Heimnetz["🏠 Home Network"]
        subgraph DMZ["DMZ Zone"]
            Pi4[Raspberry Pi 4\nGhost CMS\nNginx\nOllama]
        end
        subgraph LAN["LAN Zone"]
            Pi5[Raspberry Pi 5\nGitea\nNAS / Samba]
        end
        Pi4 <-->|Internal| Pi5
    end

    UCG -->|LAN VLAN| Pi5
    Dev([💻 MacBook\nDevelopment]) -->|SSH / local| Pi5
    Dev -->|SSH / local| Pi4

What's running here:

Why Cloudflare Is the Only External Dependency

No open port in the router. No DynDNS. No self-managed TLS certificate that expires and causes a 3am outage. The Cloudflare Tunnel establishes an outbound connection from my Pi to Cloudflare's infrastructure — no port is exposed externally, there's no direct attack surface.

That's a deliberate security decision, not a convenience. Running a home server with an open port 443 exposes you to a very different risk profile than someone who applies Zero Trust as an architectural principle.

Security That Holds Its Own Against Corporate Environments

I occasionally hear the objection: "A home lab isn't a serious setup." I disagree.

What's implemented here:

With two users, there's really only one word to say about performance: enough.


The Stack: How the Pieces Fit Together

flowchart TD
    classDef cloud   fill:#1e3a5f,color:#93c5fd,stroke:#93c5fd,stroke-width:1.5px
    A[📝 Write article\nin Ghost] -->|Webhook| B[Gitea\nRepository]
    B -->|Trigger| C[Gitea Actions\nCI/CD Pipeline]
    C -->|Build| D[Astro\nStatic Site\nGenerator]
    D -->|Deploy| E[Dev instance\nfor review]
    E -->|QG1: Code review| F[👤 Approval 1]
    F -->|QG2: Visual review| G[👤 Approval 2]
    G -->|Deploy| H[Nginx Prod\nDocker]
    H -->|Cache invalidation| I[Cloudflare\nTunnel]
    I -->|HTTPS| J[🌐 Reader]

    class F cloud
    class G cloud

Ghost runs as a headless CMS in a Docker container — no theme, just the Content API. Articles are written and managed in Ghost, but the frontend is completely decoupled from it.

Astro with the AstroPaper theme generates static HTML pages from the Ghost content. Static means: no server-side rendering, no PHP, no database queries on page load. Fast, secure, low maintenance.

Gitea Actions handles the CI/CD pipeline: Ghost fires a webhook to trigger a build, Gitea Actions pulls the content via the Ghost Content API, Astro builds the static site. Two quality gates stand between writing and publishing — nothing goes live without explicit approval.


Claude Code in Action: What I Actually Did

Now for the actual subject. How did I use Claude Code during the build — and what did I learn from it?

The Ground Rule: Only What I Can Evaluate

I set myself a clear rule: Claude Code only gets involved where I can judge the result myself. That sounds obvious — but in practice it isn't always. The temptation is real to just take a generated YAML block because it "looks plausible."

I deliberately didn't do that. Every step was approved only after I understood what it does.

What Claude Code Did Well

Gitea Actions workflows were a clear strength. YAML syntax is verbose and error-prone to write — a single wrong space is enough to break everything. In operation it's very stable and reliable. Claude Code delivered working scaffolding that I then adapted and reviewed.

# Example: Generated workflow scaffold for 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 configurations for reverse proxy and SSL termination — Claude Code provided solid starting points. The Cloudflare-specific headers I had to add myself — exactly the scenario I described in Article 1: Claude Code doesn't know my context unless I provide it.

Bash scripts for deployment, backup, and monitoring — this is where Claude Code was most reliable. Contained tasks, well-defined inputs and outputs, easy to review.

Where I Had to Correct Things

Security-relevant configurations — this is where I was most critical. Claude Code generated Nginx configurations that were technically correct but not security-hardened. Missing security headers, overly permissive CORS settings, default values that have no place in production.

That's not a criticism of Claude Code. It's confirmation of the core argument from Article 1: the model doesn't know my security requirements unless I explicitly put them in the prompt.

Gitea-specific quirks — Gitea Actions is very similar to GitHub Actions, but not identical. Claude Code generated GitHub Actions syntax multiple times that simply doesn't work in Gitea. After the first time, I always mentioned it explicitly in the prompt: "This runs on Gitea Actions, not GitHub Actions."

Infrastructure as Code structure — Claude Code produced working but unmaintainable scripts. Too many hardcoded values, no parameterisation, no error handling. I always cleaned that up — and learned a lot in the process.

What I Took Away From It

This is the actually valuable part. Not the generated code — but what I learned through critically reviewing it.

When I review an Nginx block that Claude Code generated, I have to understand why every directive is there. That forces me to go deeper into the documentation than I might have without the AI starting point. Claude Code didn't take the work away from me — it gave me something to think about.

That's the usage pattern I'd recommend: Claude Code as a conversation partner and starting point, not as an autopilot.


The Result: A Pipeline That Works

After several weeks of building, review cycles, and adjustments, the stack is running stably. Here's the full deployment flow with both quality gates:

sequenceDiagram
    participant T as Thomas
    participant G as Ghost CMS
    participant GA as Gitea Actions
    participant AS as Astro Build
    participant DEV as Dev instance
    participant AP1 as Quality Gate 1\nCode Review
    participant AP2 as Quality Gate 2\nVisual Review
    participant PROD as Nginx Prod
    participant CF as Cloudflare

    T->>G: Publish article
    G->>GA: Fire webhook
    GA->>AS: Trigger Astro build
    AS->>AP1: Build complete → Code review
    T->>AP1: ✅ Approve code
    AP1->>DEV: Deploy to dev instance
    T->>DEV: Visual review
    T->>AP2: ✅ Approve visually
    AP2->>PROD: Deploy to production
    PROD->>CF: Cache invalidation via API
    Note over CF: Readers see new version

Two quality gates — code review and visual review on the dev instance — before anything goes to production. Every arrow in this diagram is a step I understand, have reviewed, and can stand behind. That was the goal.


Conclusion: Build It Yourself, Understand It Yourself, Own It Yourself

What I took away from this project goes beyond the tech stack.

First: a home lab can — with the right architectural approach — be a serious infrastructure. Zone architecture, Zero Trust, Infrastructure as Code, automated pipelines with manual gates — none of that is an enterprise exclusive.

Second: Claude Code is an excellent tool for exactly this kind of context — when used correctly. As a starting point, as a conversation partner, as an accelerator for tasks you can evaluate yourself. Not as a substitute for your own understanding.

Third: the review process isn't the overhead — it's the value. I learned more about Nginx, Gitea Actions, and Bash by critically working through Claude Code output than I would have from passively reading documentation.

Self-hosting isn't nostalgia or protest for me. It's a deliberate choice for control, understanding, and sovereignty — over my data, my infrastructure, and my tech stack.


This article reflects my personal views exclusively and has no connection to any professional affiliation.


Artikel teilen:

Nächster Artikel
Masterbuilt Gravity 1050: Bypass Panel — vom Design zum fertigen Teil