Skip to content

Architecture

System Design

This homelab runs on Kubernetes (k3s) with GitOps principles using Flux. All infrastructure and applications are defined as code in this repository.

graph TB
    subgraph Cluster["Kubernetes Cluster (k3s)"]
        Flux["Flux CD<br/>(GitOps)"]
        Apps["Applications<br/>(Deployments)"]
        Infra["Infrastructure<br/>(Traefik, Cert-Manager, etc.)"]
        Data["Storage<br/>(Longhorn PVCs)"]
    end

    External["External Services"]
    DNS["DNS<br/>(Pi-hole)"]
    Git["Git Repository<br/>(source of truth)"]

    Git -->|watches| Flux
    Flux -->|reconciles| Apps
    Flux -->|reconciles| Infra
    External -.->|ingress| Infra
    Apps -->|reads/writes| Data
    External -->|DNS| DNS

Key Components

Core Kubernetes

Component Purpose
k3s Lightweight Kubernetes distribution
Flux CD GitOps controller; syncs desired state from git
Traefik Ingress controller & reverse proxy
Cert-Manager Automatic TLS certificate provisioning

Observability Stack

Component Purpose
Prometheus Metrics collection & time-series database
Grafana Metrics visualization & dashboards
Loki Centralized log aggregation
Promtail Log shipper (Prometheus agent for logs)

Storage

Component Purpose
Longhorn Distributed block storage for PVCs
Kopia Backup & disaster recovery

Security & Networking

Component Purpose
Pi-hole DNS filtering & ad-blocking
Authelia / LLDAP Authentication & directory services
CrowdSec Threat detection & intrusion prevention
Cloudflare Tunnel Secure external access

Deployment Patterns

Kustomize

Apps and infrastructure are split into:

  • base/ — Generic, reusable manifests
  • production/ (or other overlays) — Environment-specific patches & customizations

Example structure:

apps/base/mkdocs/
├── kustomization.yaml   # Base defines namespace, images, resources
├── deployment.yaml
├── service.yaml
└── ingress.yaml

apps/production/
└── kustomization.yaml   # Overlays: patch replicas, add monitoring, etc.

Flux

Flux watches the repository and reconciles:

  1. Infrastructure — Core services (Traefik, Cert-Manager, etc.)
  2. Applications — User apps (Immich, Bookstack, etc.)

Both sync from git on intervals (default: 1 minute).

Helm Releases

Some components use Helm (e.g., Immich):

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: immich
spec:
  chart:
    spec:
      chart: immich
      sourceRef:
        name: immich-charts
  values:
    # Helm value overrides

Network Flow

  1. External request → DNS (Pi-hole) → Traefik (ingress)
  2. Traefik → routes to service IP (ClusterIP/LoadBalancer)
  3. Service → load balances to pod replicas
  4. Pod → accesses storage (PVCs backed by Longhorn)

TLS/HTTPS

  • Cert-Manager watches Ingress objects for TLS cert annotations
  • Automatically provisions certs from Let's Encrypt
  • Renews before expiry

Data & Persistence

  • Database pods use Longhorn PersistentVolumeClaims
  • Regular backups via Kopia
  • StatefulSet for consistent storage (e.g., Pi-hole)
  • PVC snapshots available for disaster recovery

High Availability

Where applicable:

  • Deployments scaled to 2+ replicas for workload distribution
  • Horizontal Pod Autoscaler can scale based on metrics
  • Pod Disruption Budgets prevent simultaneous pod evictions
  • Anti-affinity spreads pods across nodes

Repository as Source of Truth

All infrastructure and app config lives in git:

git log --oneline
# Show all past changes to cluster state

This enables:

  • Auditability — full change history
  • Rollback — revert to prior state by reverting a commit
  • Code review — PRs before production changes
  • Reproducibility — redeploy entire system from scratch

See Also