Skip to main content

Production Stack Architecture

Platform Architecture β€” the technical capabilities of the minicloud platform (6-node on-premises k3s, 44 cores / ~84 GiB), in three complementary, icon-rich views: a capability map (understand in 30s), the detailed technology topology (engineering reference), and the physical / deployment view (where it runs + failure domains).

:::tip Two complementary approaches β€” pick the right lens These are the icon-rich views (mingrammer diagrams). For the formal, standard-notation C4 model of the same platform β€” System Context β†’ Container β†’ Deployment, from a single Structurizr source β€” see C4 System Design. The C4 page is the rigorous model; this page is the visual reference. Same facts, different lens. :::

:::info This view is Platform-only (by ownership, not deployment) The business applications that run on this platform (ERPNext, the digital workplace, the custom insurance LOB services…) are Information-System-owned and live in the IS Application Landscape β€” not here. A Claims app running on this cluster is still an IS application. See the Architecture Hub for how the two pillars fit together and the Platform ↔ IS dependency matrix. :::

1. Platform Capability Architecture​

The 30-second view β€” the platform's capabilities and principal flows. Boxes are capability groups (not individual products); every arrow is a verb.

Legend β€” boxes = platform capability groups; arrows = a verb; the purple box = the Information System (business apps, a separate view). It answers the six questions: how code reaches prod (β‘ β†’β‘£), how it's secured (β‘‘β‘’), how workloads are exposed (β‘‘), how secrets/data are managed (β‘’β‘€), how failures are detected (⑦), and what the platform provides to the IS (the final edge).


2. Platform Technology Topology (detailed reference)​

The full icon topology β€” generated from code (platform_overview.py, mingrammer diagrams). It's logical (a capability map, not a liveness claim). Preview below (PNG, instant); open the β–Ά vector SVG β†’ Β· PNG β†’ (both self-contained).

minicloud platform technology topology

Legend / edge vocabulary β€” solid = principal flow (request Β· delivery Β· routes-to) Β· dashed = backing dependency. Verbs: builds Β· pushes Β· promotes (CODEOWNERS PR) Β· reconciles Β· issues-certs Β· authenticates Β· supplies-secrets Β· scrapes Β· ships-logs/traces Β· backs-up Β· routes-to. Cluster-wide substrates (Cilium CNI+LB, Gatekeeper admission, Falco runtime) are labelled (cluster-wide); MinIO is off-cluster (on the MAAS controller). Every edge is verified against the GitOps manifests.


3. Platform Physical / Deployment view​

Where it actually runs β€” the 6 nodes, their roles, pinned workloads, Longhorn replication, and failure domains. Open the β–Ά vector SVG β†’ Β· PNG β†’.

minicloud platform physical / deployment view

Legend β€” set-hog is the single control-plane node (control-plane SPOF; kine/SQLite is backed up). Workers run general/AI workloads; the storage nodes (fast-heron Β· loving-gannet Β· swift-mac) hold Longhorn replicas. Vault is pinned to fast-heron; vLLM + Jitsi JVB to star-kitten. The MAAS controller is off-cluster (NAT Β· Tailscale Β· MinIO backup target). Longhorn is network-attached, so a pod fails over to any node β€” the hostname labels show current placement, not a hard pin.

The original hand-made HTML poster was retired 2026-10-17 β€” these code-generated views (and the C4 System Design) supersede it.


Platform at a Glance​

DimensionValue
Nodes5 ThinkPads + MacBook Pro 2012 β€” 44 cores / ~84 GiB (1 control-plane + 5 workers)
k3s versionv1.36.3+k3s1 (all 6 nodes)
ArgoCD apps~93 live applications
PrometheusRule objects53 (monitoring ns + podinfo)
Grafana dashboards43+
GitOps repos11 (dev images β†’ Harbor via Tailscale; prod images β†’ ghcr.io, SHA-tagged)
Phases complete0–90

Architecture Layers​

Access Layer​

All traffic enters through a single MetalLB IP (10.0.0.200). Two paths:

  • Public (devandre.sbs): Cloudflare CDN β†’ Cloudflare Tunnel (k8s Deployment, 2 replicas, cloudflare-tunnel ns, anti-affinity spread across workers)
  • Internal/VPN: Tailscale β†’ NGINX Ingress directly via *.10.0.0.200.nip.io

Every application is protected by Authentik SSO (OIDC + TOTP) via NGINX forward-auth ingress annotations. cert-manager handles TLS for both Let's Encrypt (public) and the minicloud root CA (internal).

:::note Cloudflare Tunnel β€” HA since 2026-08-13 Cloudflare Tunnel moved from a controller systemd service to a 2-replica Kubernetes Deployment (PRs #678+#679). Tunnel remains active during controller reboots. SSH backup path: controller.devandre.sbs β†’ ssh://10.0.0.1:22 (PR #680). :::

GitOps & Automation​

:::note Promotion is now Kargo-driven (staging retired) The flow below is the historical CI-drives-promotion shape. Since 2026-08, promotion is trunk-based: CI on main builds + proves one immutable artifact, Kargo promotes it dev β†’ prod (CODEOWNERS-gated), and Argo CD deploys it. The staging environment/branch was removed. See the Delivery Workflow and Kargo pages for the current model. :::

Developer push β†’ GitHub Actions (CI)
β”œβ”€β”€ Build β†’ Trivy scan β†’ Cosign sign β†’ syft SBOM
β”œβ”€β”€ Push β†’ Harbor (via Tailscale tag:ci)
└── bump-gitops (GPG-signed PR) β†’ ArgoCD auto-sync
β”‚
β”œβ”€β”€ canary-load (parallel) β€” k6 5 min sustained
└── rollout-gate (parallel) β€” polls Rollout phase
β”‚
└── promote-staging (needs both pass)

ArgoCD manages ~93 apps via the app-of-apps pattern from minicloud-gitops. All Helm values live in helm-values/, never in minicloud-ansible/.

Argo Rollouts handles progressive delivery:

  • platform-demo: Canary 50% β†’ AnalysisRun (Prometheus gates) β†’ 100%
  • minicloud-plane: BlueGreen prePromotion (health) + postPromotion (Prometheus)

Security​

ComponentRole
Vault (HashiCorp CE)PKI, secrets, AWS KMS auto-unseal, fast-heron pinned
ESOExternalSecrets from Vault KV to k8s Secrets
Gatekeeper OPA18 constraints: require-non-root, no-privilege-escalation, no-hostpath, block-capabilities, require-seccomp, and more
FalcoRuntime syscall monitoring, DaemonSet
cert-managerLet's Encrypt (public) + minicloud CA (internal)
VaultwardenTimshel fork 1.34.1-6, SSO via Authentik
NetworkPoliciesdefault-deny-ingress on all namespaces
kube-benchk3s-CIS-1.7: 16/16 PASS on all workers
Chaos MeshFault injection for Game Day reliability testing
cosign + SBOMSupply-chain: every prod image (ghcr.io) is signed + has SBOM attached

AI / ML Platform​

User-facing AI interfaces:
Open WebUI (chat.devandre.sbs) β€” conversational chat, RAG, model switching
Flowise 2.2.7 (flowise.devandre.sbs) β€” visual LLM flow builder, no-code pipelines
β”‚
β””β†’ LiteLLM Proxy (2 replicas, KEDA auto-scale)
β”œβ†’ vLLM v0.6.6 (Phi-3-mini, on-cluster)
β”œβ†’ Groq API (llama-3.1-8b-instant, primary for phi3-financial)
β”œβ†’ OpenAI text-embedding-3-small (embeddings, 1536-dim)
β”œβ†’ DeepSeek, Mistral (cloud, via LiteLLM routes)
β””β†’ minicloud-agent / minicloud-crew-agent (OpenAI-compat endpoints)

RAG pipeline:
Document β†’ markitdown-proxy (PDF/Office/images β†’ Markdown via Docling)
β†’ rag-ingest (chunk β†’ embed β†’ store)
β†’ LiteLLM β†’ OpenAI text-embedding-3-small (1536-dim)
β†’ Qdrant (policy-docs, incident-reports, regulatory collections)

AI agents:
minicloud-agent 1.0.1 β€” LangChain single-agent, MCP tool layer
minicloud-crew-agent 1.0.1 β€” CrewAI multi-agent, insurance workflow automation

Observability:
Langfuse: TTFT, tokens/s, cost/request, prompt eval scores, 90-day trace retention
LiteLLM Prometheus: litellm_proxy_total_requests_metric_total, input tokens
vLLM dashboard: vllm-inference-v1 (7 panels, ServiceMonitor in ai ns)

Specialised AI services (all in ai namespace, managed under litellm ArgoCD app):

ServiceVersionPurpose
vLLMv0.6.6GPU-optimised LLM inference (CPU on-cluster)
MLflowv2.16.0-psycopg2Experiment tracking, model registry
Flowise2.2.7Visual LLM flow builder
minicloud-agent1.0.1LangChain agent + MCP
minicloud-crew-agent1.0.1CrewAI multi-agent
PresidiolatestPII detection + anonymisation
Langfuse3.xLLM observability (ClickHouse backend)
Open WebUIcustomChat interface, French BM25 RAG
Qdrant1.xVector store (RAG collections)

phi3-financial PromptOps pipeline: routes through groq/llama-3.1-8b-instant (primary, weight 10) with vLLM phi3-mini as fallback. 25/25 CI eval cases pass.

Automation & Workflows​

ServiceNamespaceURLNotes
n8n 2.32.7automationn8n.devandre.sbsPhase 83, 5Gi Longhorn RWO
Temporal v1.31.2temporaltemporal.devandre.sbsPhase 84, 4 historyShards (PERMANENT), PostgreSQL backend

Observability​

The observability stack uses five complementary tools:

ToolPurposeKey metric
Prometheusmetrics collection + alerting53 PrometheusRule objects
Grafanadashboards + explore43+ dashboards, TraceQL, LogQL
Lokilog aggregationOTLP native, 30-day retention, LogQL ruler
Tempo 2.9.0distributed tracingTraceQL, tracesToLogsV2, 14-day retention
OTel Collectorlog + trace shippingDaemonSet, OTTL transforms, replaces Promtail
Alertmanageralert routing→ Stalwart:587 → SES → kanmegnea@devandre.sbs + healthchecks.io watchdog

Log pipeline (Gaps 1–7 closed):

  • Promtail replaced by OTel Collector DaemonSet (otelcol-contrib 0.156.0)
  • OTTL transformprocessor: Kopia job cardinality collapsed, level label extracted and normalised
  • Controller logs via Promtail Docker container (--net=host for MetalLB routing)

SLO & Regression Detection:

  • 7 SLO objectives, 11 recording rules, burn-rate 2-tier alerts
  • 5 regression alerts comparing current vs offset 1h baseline (latency, error rate, throughput, CPU, memory)
  • RolloutAutoRollback alert on argo_rollout_info{phase="Degraded"} == 1 for 1m

Operational resilience:

  • healthchecks.io: 3 checks β€” Alertmanager watchdog (cc3f232b), controller heartbeat (b2ac9ab5), recovery failures (6a37bfa6)
  • minicloud-ops: Python recovery check, 15 health probes, systemd-managed on controller

Collaboration & Business Apps​

ServiceURLNotes
Stalwart Mailmail.devandre.sbsv0.16.13, JMAP API, RocksDB
AWS SESeu-west-1outbound relay + inbound pipeline (production approved)
Matrix + Elementelement.devandre.sbsSynapse v1.156.0, dedicated postgresql-synapse in chat ns
ERPNexterp.devandre.sbsv16.28.0, French insurance config (PCG 2025, TSCA, LOBs, Factur-X)
Jitsi Meetmeet.devandre.sbsv2.21.0, JVB pinned star-kitten, TURN = Lightsail 54.171.137.209
Nextcloudfiles.devandre.sbsOnlyOffice DocumentServer, SSO
Plane CEplane.devandre.sbsproject management
Vaultwardenvault-pw.devandre.sbsTimshel fork (SSO button), human credential store
Docusealsign.devandre.sbse-Signature (eIDAS Simple), 4 insurance templates
Flowiseflowise.devandre.sbsvisual LLM flow builder (ai ns)
MLflowmlflow.devandre.sbsML experiment tracking + model registry (ai ns)
n8nn8n.devandre.sbsworkflow automation
Temporaltemporal.devandre.sbsdurable workflow engine v1.31.2

Email flows (bidirectional, 2026-07-26):

  • Outbound: Stalwart SMTP:587 β†’ ses-relay β†’ SES eu-west-1 β†’ recipient
  • Inbound: External β†’ MX inbound-smtp.eu-west-1.amazonaws.com β†’ SES Receipt β†’ S3 β†’ SNS β†’ SQS β†’ ses-ingest pod β†’ Stalwart β†’ mailbox

Storage​

StoreLocationUsed by
LonghornDistributed (swift-mac preferred)All stateful workloads with RWO/RWX PVCs
MinIOMAAS Controller (Docker)Velero backup bucket, kine SQLite backups, S3-compat
Qdrantai ns (Longhorn PVC)RAG vector collections (1536-dim text-embedding-3-small via OpenAI)
PostgreSQL-aiai nsMLflow, LiteLLM cache, RAG metadata
postgresql-synapsechat ns, set-hog pinnedMatrix-synapse dedicated DB (migrated 2026-08-07)
ClickHouselangfuse nsLLM trace storage
MariaDBerp nsERPNext data

Backup layers:

  • Velero (daily): Longhorn snapshots β†’ MinIO velero/ bucket (local)
  • Velero off-site (weekly): Cloudflare R2 minicloud-velero-offsite bucket (72h TTL)
  • kine SQLite (daily): sqlite3 .backup β†’ MinIO k3s-backup/ + controller timer β†’ db-backups/kine/

multipathd gotcha (all 6 nodes): IET VIRTUAL-DISK devices blacklisted in /etc/multipath.conf to prevent Longhorn iSCSI volumes from being claimed as mpatha.


Hardware​

NodeIPRoleNotes
MAAS Controller (ThinkPad X390)100.88.123.8NAT, MAAS, Tailscale, MinIO, cloudflared systemd (SSH backup path)98G NVMe
set-hog10.0.0.2k3s control-planeKine/SQLite, socat proxy for scheduler+ctrl-mgr metrics
fast-skunk10.0.0.4k3s workerGeneral workloads, NVMe-first boot (BIOS)
fast-heron10.0.0.7k3s workerVault pinned (nodeSelector)
star-kitten10.0.0.8k3s worker (ai,worker)Jitsi JVB pinned (hostNetwork), vLLM inference
loving-gannet10.0.0.9k3s worker (storage,worker)ThinkPad T490, Longhorn replica-holding (added after original build)
swift-mac10.0.0.10k3s worker (storage,worker)MacBook Pro 13" 2012, Ubuntu 22.04, Longhorn preferred

Boot order: Controller (30s) β†’ cluster nodes (2 min) β†’ Tailscale on Mac.

NVMe boot fix (2026-08-13): All 5 ThinkPads boot NVMe-first. fast-skunk via BIOS; fast-heron/star-kitten/set-hog/loving-gannet via efibootmgr --create.

Power failure recovery: NAT is automated (restore-cluster-nat.service on controller β€” no manual step needed). MinIO requires a manual restart after disk-full events (caches error state in memory):

ssh controller "docker restart minio"

Key Design Decisions​

Why on-premises? Portfolio-grade work demonstrating infrastructure ownership, cost awareness, and operational discipline beyond managed cloud. All compute, networking, and storage decisions are explicit.

Why app-of-apps? Single ArgoCD application (minicloud-gitops) owns everything. New services are added by dropping an apps/<name>.yaml file β€” no manual ArgoCD config.

Why a hybrid Harbor + ghcr.io registry? Since 2026-08-27 the split is deliberate: dev/ephemeral images live in Harbor (on-premises via Tailscale β€” air-gap-capable, Trivy CVE scanning, keep-10 retention + daily GC to bound controller disk), while prod images live in ghcr.io (free, durable, off local disk, SHA-tagged + Cosign-signed with SBOM attached). This keeps the controller's disk budget bounded without giving up supply-chain guarantees on production artifacts.

Why OTel Collector instead of Promtail? Stack-agnostic β€” only the exporter block changes when swapping backends. OTTL transforms handle cardinality normalization and label extraction without coupling to Loki's scrape config format.

Why Stalwart + SES instead of a managed email provider? Full control over deliverability, DKIM rotation, and JMAP automation. SES production access (50k msg/day) eliminates sandbox restrictions at near-zero cost.

Why Qdrant over pgvector? Dedicated vector DB with named collections, HNSW indexing, and snapshot API. pgvector is still running in postgresql-ai for legacy RAG metadata; new collections use Qdrant with OpenAI text-embedding-3-small (1536-dim, routed via LiteLLM).

Why Cloudflare Tunnel in k8s (not systemd)? 2-replica Deployment with pod anti-affinity eliminates the ~90s devandre.sbs outage that occurred on every controller reboot. Cloudflare load-balances across connectors automatically.