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 | rke2-ingress-nginx (RKE2-bundled) + ModSecurity WAF |
| TLS & PKI | cert-manager + Let's Encrypt, Evertrust Horizon/Stream CLM + internal PKI |
| Storage | Longhorn (block on FusionIO, 3-replica) + Ceph (in-cluster bulk via Rook, 3-replica) + Garage (S3) + TrueNAS (NFS/iSCSI via democratic-csi, currently idle) |
| Secrets | Akeyless with customer fragment on CipherTrust Manager (+ OpenBao failover plane) |
| Identity | Keycloak (OIDC/SSO) + OpenLDAP |
| DNS | five planes — BIND · Technitium · unbound · OpenNIC · NextDNS |
| docker-mailserver — self-hosted platform | |
| Member services | portal & marketplace at start.mdapi.ch |
| Image updates | Keel (digest-based polling) |
| CI/CD | GitLab (self-hosted EE) with Grype+Syft CVE scanning |
| Observability | VictoriaMetrics stack (vmagent · vmsingle · vmalert · vmalertmanager) + standalone Grafana · Cribl + OpenObserve (indexed logs) |
| Automation | Windmill (CI/CD, backups, and infrastructure health automation) |
| Edge router | BPI-R4 running OpenWrt 25.12 (custom fork) |
| External access | Per-service DNAT across multiple public WAN IPs (each service its own IP:port) + haproxy (TLS termination for select TCP services) |
| IPv6 edge | NAT64 (Jool) into the v4-only cluster + native-v6 public DNS via dnsdist |
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)"]
dnat["Per-service DNAT + haproxy<br/>(TLS termination for select services)"]
nat64["Jool NAT64 + dnsdist<br/>IPv6 edge"]
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/>rke2-ingress-nginx + ModSecurity"]
apps["Applications<br/>GitLab · OwnCloud · Paperless · Keycloak<br/>Home Assistant · Plex · Joplin · …"]
obs["Observability<br/>VictoriaMetrics + vmalert<br/>Grafana · 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/>2 nodes external<br/>+ in-cluster witness"]
truenas["TrueNAS<br/>salt + pepper"]
end
subgraph sec["Secret store"]
akeyless["Akeyless SaaS<br/>+ CipherTrust customer fragment<br/>(on-premise)"]
end
clients --> dnat --> ingress --> apps
clients -->|"IPv6"| nat64 --> ingress
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¶
Two Kubernetes clusters, both 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 |
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.
Live surfaces¶
This site is the deep dive. The platform also exposes two front ends and a public status page:
- mdapi.ch — the public showcase: what the platform runs, the sites it hosts, and the open-source projects behind it.
- start.mdapi.ch — the members' portal: family & friends sign in via Keycloak SSO to a personalised set of apps, plus a self-service marketplace to request a mailbox, website, file space or subdomain.
- Service status — live status of the ten public-facing service groups, with incident history and subscribe-by-email/Slack/RSS.
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