A complete, self-hosted platform running an API management stack (Gravitee APIM) on a two-node Kubernetes cluster, fully driven by Git: every change goes through a pull request, a validation pipeline, then ArgoCD. Secrets never touch Git, every tool signs in through a single Keycloak realm backed by Active Directory, and everything is monitored and backed up.
This is a reference project based on a platform I designed and set up. It reproduces its architecture, technologies, versions and operating rules, with neutral values everywhere (example.com, 192.168.10.0/24, example.local): no real name, address or secret appears in it.
| Area | Implementation |
|---|---|
| GitOps | ArgoCD app of apps (root reads apps/), one Application per file, every Helm chart pinned to an exact version, prune + selfHeal |
| CI | Gitea Actions (and GitHub Actions here): gitleaks on the whole history, yamllint, kubeconform in strict mode with CRD schemas, promtool unit tests on alert rules, amtool checks of the Alertmanager routing and Teams cards |
| Secrets | OpenBao (KV v2, Raft) + External Secrets Operator with Kubernetes auth. Git only holds paths, never values |
| Ingress | HAProxy (TCP passthrough) in front of Traefik 3.7, the only component that terminates TLS (wildcard certificate) |
| SSO | Keycloak 26.7, LDAPS federation with Active Directory, one OIDC client per tool, permissions mapped from AD groups (GG-K8S-Admins, GG-K8S-Readers) |
| API management | Gravitee APIM 4.12 (console, portal, management API, gateway), PostgreSQL via CloudNativePG, Redis rate limiting, Elasticsearch analytics |
| Monitoring | kube-prometheus-stack (Prometheus + Alertmanager), Grafana with dashboards declared in Git, alerts to a Microsoft Teams channel |
| Backups | Barman Cloud (PostgreSQL WAL archiving + daily base backups), OpenBao Raft snapshots, application dumps, Velero, all to a Garage S3 server, restores tested |
| Component | Version | Chart | Namespace |
|---|---|---|---|
| Kubernetes (kubeadm), Cilium VXLAN | 1.30.14 | ||
| ArgoCD | v3.4.5 | install manifest | argocd |
| Traefik | v3.7.13 | traefik 41.6.0 | traefik |
| OpenBao | 2.7.0 | openbao 0.30.0 | openbao |
| External Secrets Operator | external-secrets 2.11.0 | external-secrets | |
| cert-manager | v1.19.5 | cert-manager v1.19.5 | cert-manager |
| CloudNativePG operator | 1.30.0 | cloudnative-pg 0.29.0 | cnpg-system |
| Barman Cloud plugin | v0.15.1 | plugin-barman-cloud 0.8.1 | cnpg-system |
| PostgreSQL clusters | 16 | cluster 0.8.1 | gitea, gravitee |
| Keycloak | 26.7.4 | keycloakx 7.3.2 | keycloak |
| oauth2-proxy | v7.15.5 | oauth2-proxy 10.7.1 | oauth2-proxy |
| Gitea | 1.27.0 | gitea 12.7.0 | gitea |
| Gitea runner + Docker in Docker | 2.0.1, 29.5.2 | actions 0.1.2 | gitea |
| Gravitee APIM | 4.12.3 | apim 4.12.3 | gravitee |
| Redis | redis 20.6.0 | gravitee | |
| Logstash | 8.5.1 | logstash 8.5.1 | gravitee |
| kube-prometheus-stack | operator v0.94.1 | kube-prometheus-stack 91.9.0 | monitoring |
| Grafana | 12.3 | grafana 10.5.15 | monitoring |
| Velero | v1.18.4 | velero 12.2.0 | velero |
| Garage (S3) | v2.4.1 | systemd service | outside the cluster |
| HAProxy | 2.x | package | outside the cluster |
bootstrap/root.yaml the only Application created by hand: it reads apps/
apps/ one ArgoCD Application per file (Helm charts or folders below)
argocd/ ArgoCD settings: OIDC, RBAC, ingress, repository credentials, metrics
secret-stores/ ClusterSecretStore "openbao"
traefik/ wildcard certificate, middlewares, PodMonitor, metrics blocking
oauth2-proxy/ client secret and callback route for the Traefik dashboard
keycloak/ database and admin secrets, Active Directory CA
gitea/ admin, database, OIDC and runner secrets
gravitee/ APIM secrets, license, Keycloak database (CNPG Database + managed role)
monitoring/ Grafana secrets, Teams webhook, alert rules, dashboards
backups/ CNPG object stores and schedules, OpenBao snapshot, Gitea and Grafana dumps
velero/ Velero S3 credentials
infra/ everything outside Kubernetes: HAProxy, Garage, firewalld
roadmap/ planned work, not deployed (external gateway with sharding tags, Coraza WAF)
ci/ tool installation (pinned, sha256 checked) and validation script
tools/ helpers: Helm rendering, CRD schemas, dashboard extraction, S3 restore
tests/ promtool unit tests and a sample alert for the Teams card templates
docs/ architecture, procedures and diagrams
The apps/*-config.yaml Applications deploy the plain manifests of the matching folder (argocd-config deploys argocd/, and so on).
mainis protected: no direct push, thevalidation / manifestscheck is mandatory, nobody can bypass it, administrators included.- One branch per change, created from an up to date
main, deleted once the pull request is merged. - Charts are pinned (
targetRevisionis an exact version). An upgrade is a pull request that changes the version, after comparinghelm templatebefore and after. - No secret, password hash or token in Git. A new secret means: value in OpenBao,
ExternalSecretin Git. - No
kubectl patchon Applications:rootruns withselfHealand reverts it. Git is the only way in. - Charts that ship large CRDs use
ServerSideApply=true(the client side annotation is limited to 256 KB).
| Document | Content |
|---|---|
| docs/architecture.md | Components, network path, hostnames, design choices |
| docs/gitops-ci.md | Change workflow, CI checks, branch protection, adding an application |
| docs/secrets.md | OpenBao layout, External Secrets, rotation procedures |
| docs/sso.md | Keycloak realm, AD federation, one client per tool |
| docs/monitoring.md | Metrics, dashboards, alerting to Teams |
| docs/backups.md | What is backed up, where, and how to restore |
| docs/bootstrap.md | Building the platform from empty machines |
| docs/operations.md | Runbooks and lessons learned from incidents |
| docs/roadmap.md | What comes next |
bash ci/install-tools.sh "$HOME/.local/bin"
export PATH="$HOME/.local/bin:$PATH"
gitleaks git --redact --no-banner .
bash ci/validate.shWith Helm installed, ~/.venv-ci/bin/python3 tools/helm-render.py render renders every chart declared in apps/ with its values, which is the best way to review a version upgrade.
MIT, see LICENSE.