Skip to content

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
Mail docker-mailserverself-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