Skip to content

Repository files navigation

HomeCloud

Self-hosted private cloud on bare metal. Talos + Cilium + ArgoCD + Longhorn + KubeVirt.

Two nodes today (1 control plane with scheduling on, 1 worker), designed to scale to a 3-node HA control plane.

Layout

Path Purpose
cluster/ Talos machine configs + imperative bootstrap (Cilium, ArgoCD). Start at cluster/README.md.
gitops/ ArgoCD's source of truth - root, infrastructure, operators, security, services, apps.
manifests/ Ad-hoc / one-shot manifests, applied manually. Not reconciled by ArgoCD.
scripts/ validate.sh and cluster.sh (restore, shutdown, start), behind the mise tasks.
network/netboot/ netboot.xyz + ProxyDHCP install notes for the planned provisioning host. Not deployed.
renovate.json5 Renovate config; runs every 4h via .github/workflows/renovate.yml.

Conventions for agents and humans: CLAUDE.md.

Setup

brew install mise op
mise install   # everything pinned in .mise.toml

op (1Password CLI) is the only required tool not managed by mise. It backs the op://homecloud/... URIs used in .env generation and in re-encrypting the bootstrap credential.

Common tasks live in .mise.toml - mise tasks lists them (render, validate, bootstrap, talos:upgrade, talos:upgrade-k8s, restore, cluster:shutdown, cluster:start, argo:sync). PRs touching gitops/ run mise run validate in .github/workflows/validate.yml.

What's running

Versions live next to the manifests - gitops/*/*/kustomization.yaml for chart versions, cluster/bootstrap/helmfile.yaml for Cilium / Gateway API / ArgoCD.

Layer Deployed
infrastructure/ wave 0 cert-manager, external-dns, external-secrets, gateway, headlamp, infra-app-httproutes, keda, kube-prometheus-stack, loki, longhorn, metrics-server, reloader, secrets
operators/ wave 5 cnpg, falco, kubevirt, mariadb, tailscale
security/ wave 10 falco, trivy
services/ wave 15 kubevirt (KubeVirt + CDI CRs)
apps/ wave 100 actual-budget, authentik, cloudflared, gatus, mealie, media-stack, n8n, portfolio

gitops/experimental/ is a staging area - no ApplicationSet reads it, so nothing in it runs. It mirrors the layers: apps/ holds homarr, outline, speedtest-tracker, uptime-kuma and the restore-test and pitr-test drill apps; infrastructure/ rancher; operators/ and services/ seaweedfs; security/ kubescape.

Roadmap

Architecture

Deployed components

flowchart TD
    classDef external fill:#f9f,stroke:#333,stroke-width:2px;
    classDef gitops fill:#bbf,stroke:#333,stroke-width:2px;
    classDef routing fill:#bfb,stroke:#333,stroke-width:1px;
    classDef compute fill:#ffb,stroke:#333,stroke-width:1px;
    classDef storage fill:#fbb,stroke:#333,stroke-width:1px;
    classDef security fill:#f99,stroke:#333,stroke-width:1px;
    classDef obs fill:#dfd,stroke:#333,stroke-width:1px;

    subgraph Ext ["External / Cloud"]
        GH[GitHub repo - nulcell/homecloud]:::external
        CF[Cloudflare DNS + Tunnel]:::external
        OP[1Password vault]:::external
        TS[Tailscale]:::external
        S3[AWS S3 - backups]:::external
    end

    subgraph GitOps ["GitOps"]
        Argo[ArgoCD]:::gitops
        ESO[External Secrets Operator]:::gitops
        GH -->|Sync manifests| Argo
        OP -->|ClusterSecretStore| ESO
        ESO -->|Materialises Secrets| Argo
    end

    subgraph Networking ["Networking & Ingress"]
        GWi[Gateway 'internal' - 10.10.20.2]:::routing
        GWe[Gateway 'external' - 10.10.20.6]:::routing
        Cilium[Cilium CNI - eBPF, kube-proxy replacement, L2 announcements]:::routing
        ExtDNS[external-dns]:::routing
        CertMan[cert-manager]:::routing
        CFD[cloudflared DaemonSet]:::routing

        CF -->|Tunnel| CFD
        CFD -->|Origin request| GWi
        TS -->|Tailnet| Cilium
        GWi --> Cilium
        GWe --> Cilium
        GWi -.->|HTTPRoute hostnames| ExtDNS
        GWe -.->|HTTPRoute hostnames| ExtDNS
        ExtDNS --> CF
        CertMan -.->|wildcard-nulcell-tls via DNS-01| GWi
        CertMan -.-> GWe
    end

    subgraph DataStore ["Storage & Databases"]
        LH[Longhorn block storage]:::storage
        CNPG[CNPG operator]:::storage
        PGDB[Postgres - authentik, gatus, mealie, n8n]:::storage
        MDB[mariadb-operator]:::storage

        CNPG -->|Manages| PGDB
        PGDB -->|Claims PVs| LH
        LH -->|Volume backups| S3
        PGDB -->|Base backups + WAL| S3
    end

    subgraph Compute ["Compute & Workloads"]
        KVirt[KubeVirt + CDI]:::compute
        subgraph Apps ["Applications"]
            Media[media-stack - Jellyfin, arr apps, Gluetun]:::compute
            N8N[n8n]:::compute
            Auth[authentik]:::compute
            Misc[mealie, actual-budget, gatus, portfolio]:::compute
        end

        Cilium --> Apps
        Cilium --> KVirt
        Apps -->|Secrets| ESO
        Apps -->|Persistent storage| LH
        Apps -->|Database| DataStore
        KVirt -->|Claims PVs| LH
    end

    subgraph SecLayer ["Security"]
        Falco[Falco - modern eBPF + k8saudit]:::security
        FB[Fluent Bit - kube-apiserver audit logs]:::security
        FSide[Falcosidekick + UI]:::security
        Talon[Falco Talon - response actions]:::security
        Trivy[Trivy Operator - vulnerability and config scans]:::security

        FB -->|Audit webhook| Falco
        Falco -->|Alerts| FSide
        FSide -->|priority >= error| Talon
    end

    subgraph Observability ["Observability"]
        KPM[kube-prometheus-stack]:::obs
        Loki[Grafana Loki]:::obs
        Alloy[Grafana Alloy]:::obs
        Grafana[Grafana]:::obs

        Compute -->|Metrics| KPM
        Compute -->|Pod logs| Alloy
        Alloy --> Loki
        Loki -.->|Datasource| Grafana
        KPM --> Grafana
        FSide -->|Alerts >= error| KPM
    end

    Trivy -->|ServiceMonitor| KPM

    Argo -.->|Deploys & manages| Networking
    Argo -.->|Deploys & manages| DataStore
    Argo -.->|Deploys & manages| Compute
    Argo -.->|Deploys & manages| SecLayer
    Argo -.->|Deploys & manages| Observability
Loading

Security (work in progress)

Detection and response run today; posture scanning is new and Falco will be tuned for its noise. Kyverno (policy) is planned.

flowchart LR
    Sys[Syscalls - modern eBPF driver]
    Audit[kube-apiserver audit log]
    FB{{Fluent Bit}}

    Audit -->|Talos audit log tail| FB
    FB -->|POST :9765/k8s-audit| Falco

    subgraph Falco ["Falco DaemonSet"]
        Rules[Rulesets - falco-rules, incubating, k8saudit]
        Plugins[Plugins - container, k8smeta, k8saudit, json]
    end

    Sys --> Falco
    Falco -->|JSON http_output :2801| Sidekick[Falcosidekick]

    Sidekick -->|WEBUI| UI[Falcosidekick UI + redis-stack]
    Sidekick -->|priority >= error| Talon[Falco Talon]
    Sidekick -->|priority >= error| AM[Alertmanager - kube-prometheus-stack]
    Talon -->|k8sevents notifier| Events[Kubernetes Events]

    subgraph Posture ["Posture scanning"]
        Trivy[Trivy Operator]
        TServer[Trivy server - vulnerability DB]
        Reports[(Report CRDs - vulnerabilities, config audit, secrets, RBAC, compliance)]

        Trivy -->|scan jobs| TServer
        Trivy --> Reports
    end

    Reports --> HL[Headlamp Trivy plugin]
    Trivy -->|ServiceMonitor| Prom[Prometheus]

    Kyverno[Kyverno - planned]:::planned
    Kyverno -.->|PolicyReports| PR[Policy Reporter - planned]:::planned
    classDef planned stroke-dasharray: 5 5;
Loading

Hardware layout

flowchart TB
    subgraph Public ["Public Internet"]
        direction LR
        PubUser[Users / Clients]
        PrivUser[Admin / Operator]
        CFD[Cloudflare DNS]
        CFT[Cloudflare Tunnel]
        TS[Tailscale ZTNA]

        PubUser -->|DNS| CFD
        PubUser -->|HTTPS| CFT
        PrivUser -->|ZTNA| TS
    end

    subgraph Network ["Home Network - 10.10.16.0/20"]
        direction TB
        Router[Main router - 10.10.31.254, DNS]
        Switch[LAN switch]

        Router --> Switch

        subgraph Cluster ["HomeCloud Kubernetes Cluster"]
            direction LR
            K8sVIP[API VIP - k8s.nulcell.com / 10.10.25.25]
            CP1[talos-tgu-9aw - control plane, 10.10.17.5]
            W1[talos-ztk-5fl - worker, 10.10.27.254]
            LB[Cilium L2 LB pool - 10.10.20.0-254]

            K8sVIP <-->|VIP| CP1
            W1 -->|API| K8sVIP
            CP1 --- LB
            W1 --- LB
        end

        Switch <-->|LAN| Cluster
    end

    Router -->|WAN| Public
    CFT -->|cloudflared to internal Gateway| Cluster
    TS -->|ZTNA| Cluster
Loading

Used by

Contributors

Languages