MDAPI Homelab¶
A self-hosted, production-grade Kubernetes homelab running on bare metal — built around GitOps, zero-trust secrets, and modern DevSecOps practices.
What is this?¶
This site documents the architecture of MDAPI (Martino Dell'Ambrogio's Personal Infrastructure), a personal homelab that runs a full-stack cloud-native environment at home. It is intended as a reference for architecture decisions, security practices, and operational patterns — not a tutorial, but a real-world example of how these technologies fit together.
For the longer story of how MDAPI started on an ISDN line in the 1990s and arrived at its current shape, see About MDAPI.
The stack is built to mirror enterprise-grade principles: declarative GitOps configuration, hardware-backed secret management, automated certificate lifecycle, container image CVE scanning, and a multi-cluster management plane.
Stack at a Glance¶
| Layer | Technology |
|---|---|
| Kubernetes distribution | Harvester HCI (RKE2 + KubeVirt) |
| Cluster management | Rancher |
| GitOps | Fleet (multi-cluster) |
| Ingress | ingress-nginx + ModSecurity WAF |
| TLS | cert-manager + Let's Encrypt (DNS-01 via RFC 2136) |
| Storage | Longhorn (block on FusionIO, 2-replica) + Ceph (in-cluster bulk via Rook) + Garage (S3) + TrueNAS (NFS/iSCSI via democratic-csi) |
| Secrets | Akeyless with customer fragment on CipherTrust Manager |
| Identity | Keycloak (OIDC/SSO) + OpenLDAP |
| Image updates | Keel (digest-based polling) |
| CI/CD | GitLab (self-hosted EE) with Grype+Syft CVE scanning |
| Observability | VictoriaMetrics (long-term metrics) + Prometheus / Alertmanager + Grafana · Cribl + OpenObserve on Ceph RGW (indexed logs) |
| Automation | Windmill (scheduled infrastructure health monitoring) |
| Edge router | BPI-R4 running OpenWrt 25.12 (custom fork) |
| External access | sslh (protocol demux: HTTPS / SSH / OpenVPN on a single port) |
Architecture at a Glance¶
Five tiers, separated by responsibility. The edge terminates external traffic; the cluster runs every workload; storage and secrets are addressed as distinct planes rather than per-service concerns. Each subsequent page in this site drills into one slice of the diagram.
flowchart TB
subgraph ext["External"]
clients["Clients<br/>web · mobile"]
offsite["Off-site backup<br/>Backblaze B2 + SFTP"]
end
subgraph edge["Edge — BPI-R4 (custom OpenWrt fork)"]
sslh["sslh<br/>HTTPS · SSH · OpenVPN<br/>demuxed on :443"]
end
subgraph k8s["Harvester HCI — RKE2 + KubeVirt — qui · quo · qua"]
plat["Platform<br/>Rancher · Fleet · cert-manager<br/>ESO · Keel · Reloader"]
ingress["Ingress + WAF<br/>ingress-nginx + ModSecurity"]
apps["Applications<br/>GitLab · OwnCloud · Paperless · Keycloak<br/>Home Assistant · Plex · Joplin · …"]
obs["Observability<br/>Prometheus + Alertmanager<br/>Cribl + OpenObserve"]
end
subgraph storage["Storage"]
longhorn["Longhorn<br/>in-cluster block on FusionIO"]
ceph["Ceph (Rook)<br/>in-cluster bulk · RWX + S3"]
garage["Garage S3<br/>3-node external"]
truenas["TrueNAS<br/>salt + pepper"]
end
subgraph sec["Secret store"]
akeyless["Akeyless SaaS<br/>+ CipherTrust customer fragment<br/>(on-premise)"]
end
clients --> sslh --> ingress --> apps
plat -.->|"declarative"| apps
plat -.->|"declarative"| ingress
apps --> longhorn
apps --> ceph
apps --> garage
garage --- truenas
longhorn -->|"backup"| garage
garage -->|"daily rclone"| offsite
apps -.->|"ExternalSecret"| akeyless
obs -.->|"observe"| apps
Solid arrows are request and data flow; dashed arrows are the management and observability planes (declarative reconcile, secret fetch, metric and log scrape).
Cluster Contexts¶
Three Kubernetes clusters, all managed through Rancher and Fleet:
| Cluster | Location | Purpose |
|---|---|---|
mdapi-rancher |
Local (Rancher management) | Fleet controller, cluster-scoped resources |
mdapi-prod |
Bare metal (homelab) | All production workloads |
mdapi-test |
Rackspace Spot | Ephemeral staging environment |
Design Principles¶
Everything is declarative. All cluster state lives in the mdapi/fleet GitLab repo at https://gitlab.mdapi.ch/mdapi/fleet (public mirror). No kubectl apply is ever run directly — every change is a git commit.
Secrets never touch git. Secrets are stored in Akeyless SaaS with an on-premise customer fragment (the plaintext never leaves the local network). Kubernetes workloads fetch them via the External Secrets Operator at runtime.
TLS everywhere, automated. cert-manager issues certificates via Let's Encrypt — DNS-01 (RFC 2136 against a self-hosted BIND9 authoritative nameserver with TSIG) for the public domains, and HTTP-01 for names under the internal-only home.tillo.ch zone.
Security is layered. ModSecurity WAF on all ingresses, OIDC for user-facing apps, CVE scanning in every CI pipeline, network-level protocol demultiplexing at the edge.
Source¶
- Fleet GitOps repo:
https://gitlab.mdapi.ch/mdapi/fleet(public mirror) - This documentation: docs.mdapi.ch · source at
https://gitlab.mdapi.ch/mdapi/docs